riched32: Fix regression in WM_GETTEXTLENGTH on richedit 1.0 emulation.
[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 #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_ReadSerializedStoreFromFile(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 (!read)
435             ; /* an empty file is okay */
436         else if (read != sizeof(fileHeaderBuf))
437             ret = FALSE;
438         else if (!memcmp(fileHeaderBuf, fileHeader, read))
439         {
440             WINE_CERT_PROP_HEADER propHdr;
441             const void *context = NULL;
442             const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
443             LPBYTE buf = NULL;
444             DWORD bufSize = 0;
445
446             do {
447                 ret = ReadFile(file, &propHdr, sizeof(propHdr), &read, NULL);
448                 if (ret && read == sizeof(propHdr))
449                 {
450                     if (contextInterface && context &&
451                      (propHdr.propID == CERT_CERT_PROP_ID ||
452                      propHdr.propID == CERT_CRL_PROP_ID ||
453                      propHdr.propID == CERT_CTL_PROP_ID))
454                     {
455                         /* We have a new context, so free the existing one */
456                         contextInterface->free(context);
457                     }
458                     if (propHdr.cb > bufSize)
459                     {
460                         /* Not reusing realloc, because the old data aren't
461                          * needed any longer.
462                          */
463                         CryptMemFree(buf);
464                         buf = CryptMemAlloc(propHdr.cb);
465                         bufSize = propHdr.cb;
466                     }
467                     if (buf)
468                     {
469                         ret = ReadFile(file, buf, propHdr.cb, &read, NULL);
470                         if (ret && read == propHdr.cb)
471                         {
472                             if (propHdr.propID == CERT_CERT_PROP_ID)
473                             {
474                                 contextInterface = pCertInterface;
475                                 ret = contextInterface->addEncodedToStore(store,
476                                  X509_ASN_ENCODING, buf, read,
477                                  CERT_STORE_ADD_NEW, &context);
478                             }
479                             else if (propHdr.propID == CERT_CRL_PROP_ID)
480                             {
481                                 contextInterface = pCRLInterface;
482                                 ret = contextInterface->addEncodedToStore(store,
483                                  X509_ASN_ENCODING, buf, read,
484                                  CERT_STORE_ADD_NEW, &context);
485                             }
486                             else if (propHdr.propID == CERT_CTL_PROP_ID)
487                             {
488                                 contextInterface = pCTLInterface;
489                                 ret = contextInterface->addEncodedToStore(store,
490                                  X509_ASN_ENCODING, buf, read,
491                                  CERT_STORE_ADD_NEW, &context);
492                             }
493                             else
494                                 ret = CRYPT_ReadContextProp(contextInterface,
495                                  context, &propHdr, buf, read);
496                         }
497                     }
498                     else
499                         ret = FALSE;
500                 }
501             } while (ret && read > 0);
502             if (contextInterface && context)
503             {
504                 /* Free the last context added */
505                 contextInterface->free(context);
506             }
507             CryptMemFree(buf);
508             ret = TRUE;
509         }
510         else
511             ret = FALSE;
512     }
513     else
514         ret = TRUE;
515     return ret;
516 }
517
518 static BOOL WINAPI CRYPT_SerializeCertNoHash(PCCERT_CONTEXT pCertContext,
519  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
520 {
521     return CRYPT_SerializeStoreElement(pCertContext,
522      pCertContext->pbCertEncoded, pCertContext->cbCertEncoded,
523      CERT_CERT_PROP_ID, pCertInterface, dwFlags, TRUE, pbElement, pcbElement);
524 }
525
526 static BOOL WINAPI CRYPT_SerializeCRLNoHash(PCCRL_CONTEXT pCrlContext,
527  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
528 {
529     return CRYPT_SerializeStoreElement(pCrlContext,
530      pCrlContext->pbCrlEncoded, pCrlContext->cbCrlEncoded,
531      CERT_CRL_PROP_ID, pCRLInterface, dwFlags, TRUE, pbElement, pcbElement);
532 }
533
534 static BOOL WINAPI CRYPT_SerializeCTLNoHash(PCCTL_CONTEXT pCtlContext,
535  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
536 {
537     return CRYPT_SerializeStoreElement(pCtlContext,
538      pCtlContext->pbCtlEncoded, pCtlContext->cbCtlEncoded,
539      CERT_CTL_PROP_ID, pCTLInterface, dwFlags, TRUE, pbElement, pcbElement);
540 }
541
542 typedef BOOL (*SerializedOutputFunc)(void *handle, const void *buffer,
543  DWORD size);
544
545 static BOOL CRYPT_SerializeContextsToStream(SerializedOutputFunc output,
546  void *handle, const WINE_CONTEXT_INTERFACE *contextInterface, HCERTSTORE store)
547 {
548     const void *context = NULL;
549     BOOL ret;
550
551     do {
552         context = contextInterface->enumContextsInStore(store, context);
553         if (context)
554         {
555             DWORD size = 0;
556             LPBYTE buf = NULL;
557
558             ret = contextInterface->serialize(context, 0, NULL, &size);
559             if (size)
560                 buf = CryptMemAlloc(size);
561             if (buf)
562             {
563                 ret = contextInterface->serialize(context, 0, buf, &size);
564                 if (ret)
565                     ret = output(handle, buf, size);
566             }
567             CryptMemFree(buf);
568         }
569         else
570             ret = TRUE;
571     } while (ret && context != NULL);
572     if (context)
573         contextInterface->free(context);
574     return ret;
575 }
576
577 static BOOL CRYPT_WriteSerializedStoreToStream(HCERTSTORE store,
578  SerializedOutputFunc output, void *handle)
579 {
580     static const BYTE fileTrailer[12] = { 0 };
581     WINE_CONTEXT_INTERFACE interface;
582     BOOL ret;
583
584     ret = output(handle, fileHeader, sizeof(fileHeader));
585     if (ret)
586     {
587         memcpy(&interface, pCertInterface, sizeof(interface));
588         interface.serialize = (SerializeElementFunc)CRYPT_SerializeCertNoHash;
589         ret = CRYPT_SerializeContextsToStream(output, handle, &interface,
590          store);
591     }
592     if (ret)
593     {
594         memcpy(&interface, pCRLInterface, sizeof(interface));
595         interface.serialize = (SerializeElementFunc)CRYPT_SerializeCRLNoHash;
596         ret = CRYPT_SerializeContextsToStream(output, handle, &interface,
597          store);
598     }
599     if (ret)
600     {
601         memcpy(&interface, pCTLInterface, sizeof(interface));
602         interface.serialize = (SerializeElementFunc)CRYPT_SerializeCTLNoHash;
603         ret = CRYPT_SerializeContextsToStream(output, handle, &interface,
604          store);
605     }
606     if (ret)
607         ret = output(handle, fileTrailer, sizeof(fileTrailer));
608     return ret;
609 }
610
611 static BOOL CRYPT_FileOutputFunc(void *handle, const void *buffer, DWORD size)
612 {
613     return WriteFile(handle, buffer, size, &size, NULL);
614 }
615
616 static BOOL CRYPT_WriteSerializedStoreToFile(HANDLE file, HCERTSTORE store)
617 {
618     SetFilePointer(file, 0, NULL, FILE_BEGIN);
619     return CRYPT_WriteSerializedStoreToStream(store, CRYPT_FileOutputFunc,
620      file);
621 }
622
623 static BOOL CRYPT_SavePKCSToMem(HCERTSTORE store,
624  DWORD dwMsgAndCertEncodingType, void *handle)
625 {
626     CERT_BLOB *blob = (CERT_BLOB *)handle;
627     CRYPT_SIGNED_INFO signedInfo = { 0 };
628     PCCERT_CONTEXT cert = NULL;
629     PCCRL_CONTEXT crl = NULL;
630     DWORD size;
631     BOOL ret = TRUE;
632
633     TRACE("(%d, %p)\n", blob->pbData ? blob->cbData : 0, blob->pbData);
634
635     do {
636         cert = CertEnumCertificatesInStore(store, cert);
637         if (cert)
638             signedInfo.cCertEncoded++;
639     } while (cert);
640     if (signedInfo.cCertEncoded)
641     {
642         signedInfo.rgCertEncoded = CryptMemAlloc(
643          signedInfo.cCertEncoded * sizeof(CERT_BLOB));
644         if (!signedInfo.rgCertEncoded)
645         {
646             SetLastError(ERROR_OUTOFMEMORY);
647             ret = FALSE;
648         }
649         else
650         {
651             DWORD i = 0;
652
653             do {
654                 cert = CertEnumCertificatesInStore(store, cert);
655                 if (cert)
656                 {
657                     signedInfo.rgCertEncoded[i].cbData = cert->cbCertEncoded;
658                     signedInfo.rgCertEncoded[i].pbData = cert->pbCertEncoded;
659                     i++;
660                 }
661             } while (cert);
662         }
663     }
664
665     do {
666         crl = CertEnumCRLsInStore(store, crl);
667         if (crl)
668             signedInfo.cCrlEncoded++;
669     } while (crl);
670     if (signedInfo.cCrlEncoded)
671     {
672         signedInfo.rgCrlEncoded = CryptMemAlloc(
673          signedInfo.cCrlEncoded * sizeof(CERT_BLOB));
674         if (!signedInfo.rgCrlEncoded)
675         {
676             SetLastError(ERROR_OUTOFMEMORY);
677             ret = FALSE;
678         }
679         else
680         {
681             DWORD i = 0;
682
683             do {
684                 crl = CertEnumCRLsInStore(store, crl);
685                 if (crl)
686                 {
687                     signedInfo.rgCrlEncoded[i].cbData = crl->cbCrlEncoded;
688                     signedInfo.rgCrlEncoded[i].pbData = crl->pbCrlEncoded;
689                     i++;
690                 }
691             } while (crl);
692         }
693     }
694     if (ret)
695     {
696         ret = CRYPT_AsnEncodePKCSSignedInfo(&signedInfo, NULL, &size);
697         if (ret)
698         {
699             if (!blob->pbData)
700                 blob->cbData = size;
701             else if (blob->cbData < size)
702             {
703                 blob->cbData = size;
704                 SetLastError(ERROR_MORE_DATA);
705                 ret = FALSE;
706             }
707             else
708             {
709                 blob->cbData = size;
710                 ret = CRYPT_AsnEncodePKCSSignedInfo(&signedInfo, blob->pbData,
711                  &blob->cbData);
712             }
713         }
714     }
715     CryptMemFree(signedInfo.rgCertEncoded);
716     CryptMemFree(signedInfo.rgCrlEncoded);
717     TRACE("returning %d\n", ret);
718     return ret;
719 }
720
721 static BOOL CRYPT_SavePKCSToFile(HCERTSTORE store,
722  DWORD dwMsgAndCertEncodingType, void *handle)
723 {
724     CERT_BLOB blob = { 0, NULL };
725     BOOL ret;
726
727     TRACE("(%p)\n", handle);
728
729     ret = CRYPT_SavePKCSToMem(store, dwMsgAndCertEncodingType, &blob);
730     if (ret)
731     {
732         blob.pbData = CryptMemAlloc(blob.cbData);
733         if (blob.pbData)
734         {
735             ret = CRYPT_SavePKCSToMem(store, dwMsgAndCertEncodingType, &blob);
736             if (ret)
737                 ret = WriteFile(handle, blob.pbData, blob.cbData,
738                  &blob.cbData, NULL);
739         }
740         else
741         {
742             SetLastError(ERROR_OUTOFMEMORY);
743             ret = FALSE;
744         }
745     }
746     TRACE("returning %d\n", ret);
747     return ret;
748 }
749
750 static BOOL CRYPT_SaveSerializedToFile(HCERTSTORE store,
751  DWORD dwMsgAndCertEncodingType, void *handle)
752 {
753     return CRYPT_WriteSerializedStoreToFile(handle, store);
754 }
755
756 struct MemWrittenTracker
757 {
758     DWORD cbData;
759     BYTE *pbData;
760     DWORD written;
761 };
762
763 /* handle is a pointer to a MemWrittenTracker.  Assumes its pointer is valid. */
764 static BOOL CRYPT_MemOutputFunc(void *handle, const void *buffer, DWORD size)
765 {
766     struct MemWrittenTracker *tracker = (struct MemWrittenTracker *)handle;
767     BOOL ret;
768
769     if (tracker->written + size > tracker->cbData)
770     {
771         SetLastError(ERROR_MORE_DATA);
772         /* Update written so caller can notify its caller of the required size
773          */
774         tracker->written += size;
775         ret = FALSE;
776     }
777     else
778     {
779         memcpy(tracker->pbData + tracker->written, buffer, size);
780         tracker->written += size;
781         ret = TRUE;
782     }
783     return ret;
784 }
785
786 static BOOL CRYPT_CountSerializedBytes(void *handle, const void *buffer,
787  DWORD size)
788 {
789     *(DWORD *)handle += size;
790     return TRUE;
791 }
792
793 static BOOL CRYPT_SaveSerializedToMem(HCERTSTORE store,
794  DWORD dwMsgAndCertEncodingType, void *handle)
795 {
796     CERT_BLOB *blob = (CERT_BLOB *)handle;
797     DWORD size;
798     BOOL ret;
799
800     ret = CRYPT_WriteSerializedStoreToStream(store, CRYPT_CountSerializedBytes,
801      &size);
802     if (ret)
803     {
804         if (!blob->pbData)
805             blob->cbData = size;
806         else if (blob->cbData < size)
807         {
808             SetLastError(ERROR_MORE_DATA);
809             blob->cbData = size;
810             ret = FALSE;
811         }
812         else
813         {
814             struct MemWrittenTracker tracker = { blob->cbData, blob->pbData,
815              0 };
816
817             ret = CRYPT_WriteSerializedStoreToStream(store, CRYPT_MemOutputFunc,
818              &tracker);
819             if (!ret && GetLastError() == ERROR_MORE_DATA)
820                 blob->cbData = tracker.written;
821         }
822     }
823     TRACE("returning %d\n", ret);
824     return ret;
825 }
826
827 BOOL WINAPI CertSaveStore(HCERTSTORE hCertStore, DWORD dwMsgAndCertEncodingType,
828  DWORD dwSaveAs, DWORD dwSaveTo, void *pvSaveToPara, DWORD dwFlags)
829 {
830     BOOL (*saveFunc)(HCERTSTORE, DWORD, void *);
831     void *handle;
832     BOOL ret;
833
834     TRACE("(%p, %08x, %d, %d, %p, %08x)\n", hCertStore,
835           dwMsgAndCertEncodingType, dwSaveAs, dwSaveTo, pvSaveToPara, dwFlags);
836
837     switch (dwSaveAs)
838     {
839     case CERT_STORE_SAVE_AS_STORE:
840     case CERT_STORE_SAVE_AS_PKCS7:
841         break;
842     default:
843         WARN("unimplemented for %d\n", dwSaveAs);
844         SetLastError(ERROR_INVALID_PARAMETER);
845         return FALSE;
846     }
847     switch (dwSaveTo)
848     {
849     case CERT_STORE_SAVE_TO_FILE:
850         handle = pvSaveToPara;
851         saveFunc = dwSaveAs == CERT_STORE_SAVE_AS_STORE ?
852          CRYPT_SaveSerializedToFile : CRYPT_SavePKCSToFile;
853         break;
854     case CERT_STORE_SAVE_TO_FILENAME_A:
855         handle = CreateFileA((LPCSTR)pvSaveToPara, GENERIC_WRITE, 0, NULL,
856          CREATE_ALWAYS, 0, NULL);
857         saveFunc = dwSaveAs == CERT_STORE_SAVE_AS_STORE ?
858          CRYPT_SaveSerializedToFile : CRYPT_SavePKCSToFile;
859         break;
860     case CERT_STORE_SAVE_TO_FILENAME_W:
861         handle = CreateFileW((LPCWSTR)pvSaveToPara, GENERIC_WRITE, 0, NULL,
862          CREATE_ALWAYS, 0, NULL);
863         saveFunc = dwSaveAs == CERT_STORE_SAVE_AS_STORE ?
864          CRYPT_SaveSerializedToFile : CRYPT_SavePKCSToFile;
865         break;
866     case CERT_STORE_SAVE_TO_MEMORY:
867         handle = pvSaveToPara;
868         saveFunc = dwSaveAs == CERT_STORE_SAVE_AS_STORE ?
869          CRYPT_SaveSerializedToMem : CRYPT_SavePKCSToMem;
870         break;
871     default:
872         WARN("unimplemented for %d\n", dwSaveTo);
873         SetLastError(ERROR_INVALID_PARAMETER);
874         return FALSE;
875     }
876     ret = saveFunc(hCertStore, dwMsgAndCertEncodingType, handle);
877     TRACE("returning %d\n", ret);
878     return ret;
879 }
880
881 BOOL WINAPI CertAddSerializedElementToStore(HCERTSTORE hCertStore,
882  const BYTE *pbElement, DWORD cbElement, DWORD dwAddDisposition, DWORD dwFlags,
883  DWORD dwContextTypeFlags, DWORD *pdwContentType, const void **ppvContext)
884 {
885     const void *context;
886     DWORD type;
887     BOOL ret;
888
889     TRACE("(%p, %p, %d, %08x, %08x, %08x, %p, %p)\n", hCertStore,
890      pbElement, cbElement, dwAddDisposition, dwFlags, dwContextTypeFlags,
891      pdwContentType, ppvContext);
892
893     /* Call the internal function, then delete the hashes.  Tests show this
894      * function uses real hash values, not whatever's stored in the hash
895      * property.
896      */
897     context = CRYPT_ReadSerializedElement(pbElement, cbElement,
898      dwContextTypeFlags, &type);
899     if (context)
900     {
901         const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
902
903         switch (type)
904         {
905         case CERT_STORE_CERTIFICATE_CONTEXT:
906             contextInterface = pCertInterface;
907             break;
908         case CERT_STORE_CRL_CONTEXT:
909             contextInterface = pCRLInterface;
910             break;
911         case CERT_STORE_CTL_CONTEXT:
912             contextInterface = pCTLInterface;
913             break;
914         default:
915             SetLastError(E_INVALIDARG);
916         }
917         if (contextInterface)
918         {
919             contextInterface->setProp(context, CERT_HASH_PROP_ID, 0, NULL);
920             contextInterface->setProp(context, CERT_MD5_HASH_PROP_ID, 0, NULL);
921             contextInterface->setProp(context, CERT_SIGNATURE_HASH_PROP_ID, 0,
922              NULL);
923             if (pdwContentType)
924                 *pdwContentType = type;
925             ret = contextInterface->addContextToStore(hCertStore, context,
926              dwAddDisposition, ppvContext);
927             contextInterface->free(context);
928         }
929         else
930             ret = FALSE;
931     }
932     else
933         ret = FALSE;
934     return ret;
935 }