ole32: Add more tracing for reading and writing storage streams.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "excpt.h"
24 #include "wine/exception.h"
25 #include "crypt32_private.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
28
29 /* Some typedefs that make it easier to abstract which type of context we're
30  * working with.
31  */
32 typedef const void *(WINAPI *CreateContextFunc)(DWORD dwCertEncodingType,
33  const BYTE *pbCertEncoded, DWORD cbCertEncoded);
34 typedef BOOL (WINAPI *AddContextToStoreFunc)(HCERTSTORE hCertStore,
35  const void *context, DWORD dwAddDisposition, const void **ppStoreContext);
36 typedef BOOL (WINAPI *AddEncodedContextToStoreFunc)(HCERTSTORE hCertStore,
37  DWORD dwCertEncodingType, const BYTE *pbEncoded, DWORD cbEncoded,
38  DWORD dwAddDisposition, const void **ppContext);
39 typedef const void *(WINAPI *EnumContextsInStoreFunc)(HCERTSTORE hCertStore,
40  const void *pPrevContext);
41 typedef BOOL (WINAPI *GetContextPropertyFunc)(const void *context,
42  DWORD dwPropID, void *pvData, DWORD *pcbData);
43 typedef BOOL (WINAPI *SetContextPropertyFunc)(const void *context,
44  DWORD dwPropID, DWORD dwFlags, const void *pvData);
45 typedef BOOL (WINAPI *SerializeElementFunc)(const void *context, DWORD dwFlags,
46  BYTE *pbElement, DWORD *pcbElement);
47 typedef BOOL (WINAPI *FreeContextFunc)(const void *context);
48 typedef BOOL (WINAPI *DeleteContextFunc)(const void *context);
49
50 /* An abstract context (certificate, CRL, or CTL) interface */
51 typedef struct _WINE_CONTEXT_INTERFACE
52 {
53     CreateContextFunc            create;
54     AddContextToStoreFunc        addContextToStore;
55     AddEncodedContextToStoreFunc addEncodedToStore;
56     EnumContextsInStoreFunc      enumContextsInStore;
57     GetContextPropertyFunc       getProp;
58     SetContextPropertyFunc       setProp;
59     SerializeElementFunc         serialize;
60     FreeContextFunc              free;
61     DeleteContextFunc            deleteFromStore;
62 } WINE_CONTEXT_INTERFACE, *PWINE_CONTEXT_INTERFACE;
63
64 static const WINE_CONTEXT_INTERFACE gCertInterface = {
65     (CreateContextFunc)CertCreateCertificateContext,
66     (AddContextToStoreFunc)CertAddCertificateContextToStore,
67     (AddEncodedContextToStoreFunc)CertAddEncodedCertificateToStore,
68     (EnumContextsInStoreFunc)CertEnumCertificatesInStore,
69     (GetContextPropertyFunc)CertGetCertificateContextProperty,
70     (SetContextPropertyFunc)CertSetCertificateContextProperty,
71     (SerializeElementFunc)CertSerializeCertificateStoreElement,
72     (FreeContextFunc)CertFreeCertificateContext,
73     (DeleteContextFunc)CertDeleteCertificateFromStore,
74 };
75
76 static const WINE_CONTEXT_INTERFACE gCRLInterface = {
77     (CreateContextFunc)CertCreateCRLContext,
78     (AddContextToStoreFunc)CertAddCRLContextToStore,
79     (AddEncodedContextToStoreFunc)CertAddEncodedCRLToStore,
80     (EnumContextsInStoreFunc)CertEnumCRLsInStore,
81     (GetContextPropertyFunc)CertGetCRLContextProperty,
82     (SetContextPropertyFunc)CertSetCRLContextProperty,
83     (SerializeElementFunc)CertSerializeCRLStoreElement,
84     (FreeContextFunc)CertFreeCRLContext,
85     (DeleteContextFunc)CertDeleteCRLFromStore,
86 };
87
88 static const WINE_CONTEXT_INTERFACE gCTLInterface = {
89     (CreateContextFunc)CertCreateCTLContext,
90     (AddContextToStoreFunc)CertAddCTLContextToStore,
91     (AddEncodedContextToStoreFunc)CertAddEncodedCTLToStore,
92     (EnumContextsInStoreFunc)CertEnumCTLsInStore,
93     (GetContextPropertyFunc)CertGetCTLContextProperty,
94     (SetContextPropertyFunc)CertSetCTLContextProperty,
95     (SerializeElementFunc)CertSerializeCTLStoreElement,
96     (FreeContextFunc)CertFreeCTLContext,
97     (DeleteContextFunc)CertDeleteCTLFromStore,
98 };
99
100 /* An extended certificate property in serialized form is prefixed by this
101  * header.
102  */
103 typedef struct _WINE_CERT_PROP_HEADER
104 {
105     DWORD propID;
106     DWORD unknown; /* always 1 */
107     DWORD cb;
108 } WINE_CERT_PROP_HEADER, *PWINE_CERT_PROP_HEADER;
109
110 BOOL WINAPI CertSerializeCRLStoreElement(PCCRL_CONTEXT pCrlContext,
111  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
112 {
113     FIXME("(%p, %08lx, %p, %p): stub\n", pCrlContext, dwFlags, pbElement,
114      pcbElement);
115     return FALSE;
116 }
117
118 BOOL WINAPI CertSerializeCTLStoreElement(PCCTL_CONTEXT pCtlContext,
119  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
120 {
121     FIXME("(%p, %08lx, %p, %p): stub\n", pCtlContext, dwFlags, pbElement,
122      pcbElement);
123     return FALSE;
124 }
125
126 BOOL WINAPI CertSerializeCertificateStoreElement(PCCERT_CONTEXT pCertContext,
127  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
128 {
129     BOOL ret;
130
131     TRACE("(%p, %08lx, %p, %p)\n", pCertContext, dwFlags, pbElement,
132      pcbElement);
133
134     if (pCertContext)
135     {
136         DWORD bytesNeeded = sizeof(WINE_CERT_PROP_HEADER) +
137          pCertContext->cbCertEncoded;
138         DWORD prop = 0;
139
140         ret = TRUE;
141         do {
142             prop = CertEnumCertificateContextProperties(pCertContext, prop);
143             if (prop)
144             {
145                 DWORD propSize = 0;
146
147                 ret = CertGetCertificateContextProperty(pCertContext,
148                  prop, NULL, &propSize);
149                 if (ret)
150                     bytesNeeded += sizeof(WINE_CERT_PROP_HEADER) + propSize;
151             }
152         } while (ret && prop != 0);
153
154         if (!pbElement)
155         {
156             *pcbElement = bytesNeeded;
157             ret = TRUE;
158         }
159         else if (*pcbElement < bytesNeeded)
160         {
161             *pcbElement = bytesNeeded;
162             SetLastError(ERROR_MORE_DATA);
163             ret = FALSE;
164         }
165         else
166         {
167             PWINE_CERT_PROP_HEADER hdr;
168             DWORD bufSize = 0;
169             LPBYTE buf = NULL;
170
171             prop = 0;
172             do {
173                 prop = CertEnumCertificateContextProperties(pCertContext, prop);
174                 if (prop)
175                 {
176                     DWORD propSize = 0;
177
178                     ret = CertGetCertificateContextProperty(pCertContext,
179                      prop, NULL, &propSize);
180                     if (ret)
181                     {
182                         if (bufSize < propSize)
183                         {
184                             if (buf)
185                                 buf = CryptMemRealloc(buf, propSize);
186                             else
187                                 buf = CryptMemAlloc(propSize);
188                             bufSize = propSize;
189                         }
190                         if (buf)
191                         {
192                             ret = CertGetCertificateContextProperty(
193                              pCertContext, prop, buf, &propSize);
194                             if (ret)
195                             {
196                                 hdr = (PWINE_CERT_PROP_HEADER)pbElement;
197                                 hdr->propID = prop;
198                                 hdr->unknown = 1;
199                                 hdr->cb = propSize;
200                                 pbElement += sizeof(WINE_CERT_PROP_HEADER);
201                                 if (propSize)
202                                 {
203                                     memcpy(pbElement, buf, propSize);
204                                     pbElement += propSize;
205                                 }
206                             }
207                         }
208                         else
209                             ret = FALSE;
210                     }
211                 }
212             } while (ret && prop != 0);
213             CryptMemFree(buf);
214
215             hdr = (PWINE_CERT_PROP_HEADER)pbElement;
216             hdr->propID = CERT_CERT_PROP_ID;
217             hdr->unknown = 1;
218             hdr->cb = pCertContext->cbCertEncoded;
219             memcpy(pbElement + sizeof(WINE_CERT_PROP_HEADER),
220              pCertContext->pbCertEncoded, pCertContext->cbCertEncoded);
221         }
222     }
223     else
224         ret = FALSE;
225     return ret;
226 }
227
228 /* Looks for the property with ID propID in the buffer buf.  Returns a pointer
229  * to its header if a valid header is found, NULL if not.  Valid means the
230  * length of thte property won't overrun buf, and the unknown field is 1.
231  */
232 static const WINE_CERT_PROP_HEADER *CRYPT_findPropID(const BYTE *buf,
233  DWORD size, DWORD propID)
234 {
235     const WINE_CERT_PROP_HEADER *ret = NULL;
236     BOOL done = FALSE;
237
238     while (size && !ret && !done)
239     {
240         if (size < sizeof(WINE_CERT_PROP_HEADER))
241         {
242             SetLastError(CRYPT_E_FILE_ERROR);
243             done = TRUE;
244         }
245         else
246         {
247             const WINE_CERT_PROP_HEADER *hdr =
248              (const WINE_CERT_PROP_HEADER *)buf;
249
250             size -= sizeof(WINE_CERT_PROP_HEADER);
251             buf += sizeof(WINE_CERT_PROP_HEADER);
252             if (size < hdr->cb)
253             {
254                 SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
255                 done = TRUE;
256             }
257             else if (!hdr->propID)
258             {
259                 /* assume a zero prop ID means the data are uninitialized, so
260                  * stop looking.
261                  */
262                 done = TRUE;
263             }
264             else if (hdr->unknown != 1)
265             {
266                 SetLastError(ERROR_FILE_NOT_FOUND);
267                 done = TRUE;
268             }
269             else if (hdr->propID == propID)
270                 ret = hdr;
271             else
272             {
273                 buf += hdr->cb;
274                 size -= hdr->cb;
275             }
276         }
277     }
278     return ret;
279 }
280
281 const void *CRYPT_ReadSerializedElement(const BYTE *pbElement, DWORD cbElement,
282  DWORD dwContextTypeFlags, DWORD *pdwContentType)
283 {
284     const void *context;
285
286     TRACE("(%p, %ld, %08lx, %p)\n", pbElement, cbElement, dwContextTypeFlags,
287      pdwContentType);
288
289     if (!cbElement)
290     {
291         SetLastError(ERROR_END_OF_MEDIA);
292         return NULL;
293     }
294
295     __TRY
296     {
297         const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
298         const WINE_CERT_PROP_HEADER *hdr = NULL;
299         DWORD type = 0;
300         BOOL ret;
301
302         ret = TRUE;
303         context = NULL;
304         if (dwContextTypeFlags == CERT_STORE_ALL_CONTEXT_FLAG)
305         {
306             hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CERT_PROP_ID);
307             if (hdr)
308                 type = CERT_STORE_CERTIFICATE_CONTEXT;
309             else
310             {
311                 hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CRL_PROP_ID);
312                 if (hdr)
313                     type = CERT_STORE_CRL_CONTEXT;
314                 else
315                 {
316                     hdr = CRYPT_findPropID(pbElement, cbElement,
317                      CERT_CTL_PROP_ID);
318                     if (hdr)
319                         type = CERT_STORE_CTL_CONTEXT;
320                 }
321             }
322         }
323         else if (dwContextTypeFlags & CERT_STORE_CERTIFICATE_CONTEXT_FLAG)
324         {
325             hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CERT_PROP_ID);
326             type = CERT_STORE_CERTIFICATE_CONTEXT;
327         }
328         else if (dwContextTypeFlags & CERT_STORE_CRL_CONTEXT_FLAG)
329         {
330             hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CRL_PROP_ID);
331             type = CERT_STORE_CRL_CONTEXT;
332         }
333         else if (dwContextTypeFlags & CERT_STORE_CTL_CONTEXT_FLAG)
334         {
335             hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CTL_PROP_ID);
336             type = CERT_STORE_CTL_CONTEXT;
337         }
338
339         switch (type)
340         {
341         case CERT_STORE_CERTIFICATE_CONTEXT:
342             contextInterface = &gCertInterface;
343             break;
344         case CERT_STORE_CRL_CONTEXT:
345             contextInterface = &gCRLInterface;
346             break;
347         case CERT_STORE_CTL_CONTEXT:
348             contextInterface = &gCTLInterface;
349             break;
350         default:
351             SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
352             ret = FALSE;
353         }
354         if (!hdr)
355             ret = FALSE;
356
357         if (ret)
358             context = contextInterface->create(X509_ASN_ENCODING,
359              (BYTE *)hdr + sizeof(WINE_CERT_PROP_HEADER), hdr->cb);
360         if (ret && context)
361         {
362             BOOL noMoreProps = FALSE;
363
364             while (!noMoreProps && ret)
365             {
366                 if (cbElement < sizeof(WINE_CERT_PROP_HEADER))
367                     ret = FALSE;
368                 else
369                 {
370                     const WINE_CERT_PROP_HEADER *hdr =
371                      (const WINE_CERT_PROP_HEADER *)pbElement;
372
373                     TRACE("prop is %ld\n", hdr->propID);
374                     cbElement -= sizeof(WINE_CERT_PROP_HEADER);
375                     pbElement += sizeof(WINE_CERT_PROP_HEADER);
376                     if (cbElement < hdr->cb)
377                     {
378                         SetLastError(HRESULT_FROM_WIN32(
379                          ERROR_INVALID_PARAMETER));
380                         ret = FALSE;
381                     }
382                     else if (!hdr->propID)
383                     {
384                         /* Like in CRYPT_findPropID, stop if the propID is zero
385                          */
386                         noMoreProps = TRUE;
387                     }
388                     else if (hdr->unknown != 1)
389                     {
390                         SetLastError(ERROR_FILE_NOT_FOUND);
391                         ret = FALSE;
392                     }
393                     else if (hdr->propID != CERT_CERT_PROP_ID &&
394                      hdr->propID != CERT_CRL_PROP_ID && hdr->propID !=
395                      CERT_CTL_PROP_ID)
396                     {
397                         /* Have to create a blob for most types, but not
398                          * for all.. arghh.
399                          */
400                         switch (hdr->propID)
401                         {
402                         case CERT_AUTO_ENROLL_PROP_ID:
403                         case CERT_CTL_USAGE_PROP_ID:
404                         case CERT_DESCRIPTION_PROP_ID:
405                         case CERT_FRIENDLY_NAME_PROP_ID:
406                         case CERT_HASH_PROP_ID:
407                         case CERT_KEY_IDENTIFIER_PROP_ID:
408                         case CERT_MD5_HASH_PROP_ID:
409                         case CERT_NEXT_UPDATE_LOCATION_PROP_ID:
410                         case CERT_PUBKEY_ALG_PARA_PROP_ID:
411                         case CERT_PVK_FILE_PROP_ID:
412                         case CERT_SIGNATURE_HASH_PROP_ID:
413                         case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID:
414                         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
415                         case CERT_ENROLLMENT_PROP_ID:
416                         case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
417                         case CERT_RENEWAL_PROP_ID:
418                         {
419                             CRYPT_DATA_BLOB blob = { hdr->cb,
420                              (LPBYTE)pbElement };
421
422                             ret = contextInterface->setProp(context,
423                              hdr->propID, 0, &blob);
424                             break;
425                         }
426                         case CERT_DATE_STAMP_PROP_ID:
427                             ret = contextInterface->setProp(context,
428                              hdr->propID, 0, pbElement);
429                             break;
430                         default:
431                             FIXME("prop ID %ld: stub\n", hdr->propID);
432                         }
433                     }
434                     pbElement += hdr->cb;
435                     cbElement -= hdr->cb;
436                     if (!cbElement)
437                         noMoreProps = TRUE;
438                 }
439             }
440             if (ret)
441             {
442                 if (pdwContentType)
443                     *pdwContentType = type;
444             }
445             else
446             {
447                 contextInterface->free(context);
448                 context = NULL;
449             }
450         }
451     }
452     __EXCEPT_PAGE_FAULT
453     {
454         SetLastError(STATUS_ACCESS_VIOLATION);
455         context = NULL;
456     }
457     __ENDTRY
458     return context;
459 }
460
461 BOOL WINAPI CertAddSerializedElementToStore(HCERTSTORE hCertStore,
462  const BYTE *pbElement, DWORD cbElement, DWORD dwAddDisposition, DWORD dwFlags,
463  DWORD dwContextTypeFlags, DWORD *pdwContentType, const void **ppvContext)
464 {
465     const void *context;
466     DWORD type;
467     BOOL ret;
468
469     TRACE("(%p, %p, %ld, %08lx, %08lx, %08lx, %p, %p)\n", hCertStore,
470      pbElement, cbElement, dwAddDisposition, dwFlags, dwContextTypeFlags,
471      pdwContentType, ppvContext);
472
473     /* Call the internal function, then delete the hashes.  Tests show this
474      * function uses real hash values, not whatever's stored in the hash
475      * property.
476      */
477     context = CRYPT_ReadSerializedElement(pbElement, cbElement,
478      dwContextTypeFlags, &type);
479     if (context)
480     {
481         const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
482
483         switch (type)
484         {
485         case CERT_STORE_CERTIFICATE_CONTEXT:
486             contextInterface = &gCertInterface;
487             break;
488         case CERT_STORE_CRL_CONTEXT:
489             contextInterface = &gCRLInterface;
490             break;
491         case CERT_STORE_CTL_CONTEXT:
492             contextInterface = &gCTLInterface;
493             break;
494         default:
495             SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
496         }
497         if (contextInterface)
498         {
499             contextInterface->setProp(context, CERT_HASH_PROP_ID, 0, NULL);
500             contextInterface->setProp(context, CERT_MD5_HASH_PROP_ID, 0, NULL);
501             contextInterface->setProp(context, CERT_SIGNATURE_HASH_PROP_ID, 0,
502              NULL);
503             if (pdwContentType)
504                 *pdwContentType = type;
505             ret = contextInterface->addContextToStore(hCertStore, context,
506              dwAddDisposition, ppvContext);
507             contextInterface->free(context);
508         }
509         else
510             ret = FALSE;
511     }
512     else
513         ret = FALSE;
514     return ret;
515 }