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