crypt32: Fix indenting for szOID_CRL_DIST_POINTS.
[wine] / dlls / crypt32 / object.c
1 /*
2  * crypt32 Crypt*Object functions
3  *
4  * Copyright 2007 Juan Lang
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 #include <stdarg.h>
21 #define NONAMELESSUNION
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wincrypt.h"
25 #include "mssip.h"
26 #include "winuser.h"
27 #include "wintrust.h"
28 #include "crypt32_private.h"
29 #include "cryptres.h"
30 #include "wine/unicode.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
34
35 static BOOL CRYPT_ReadBlobFromFile(LPCWSTR fileName, PCERT_BLOB blob)
36 {
37     BOOL ret = FALSE;
38     HANDLE file;
39
40     TRACE("%s\n", debugstr_w(fileName));
41
42     file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
43      OPEN_EXISTING, 0, NULL);
44     if (file != INVALID_HANDLE_VALUE)
45     {
46         ret = TRUE;
47         blob->cbData = GetFileSize(file, NULL);
48         if (blob->cbData)
49         {
50             blob->pbData = CryptMemAlloc(blob->cbData);
51             if (blob->pbData)
52             {
53                 DWORD read;
54
55                 ret = ReadFile(file, blob->pbData, blob->cbData, &read, NULL);
56             }
57         }
58         CloseHandle(file);
59     }
60     TRACE("returning %d\n", ret);
61     return ret;
62 }
63
64 static BOOL CRYPT_QueryContextObject(DWORD dwObjectType, const void *pvObject,
65  DWORD dwExpectedContentTypeFlags, DWORD *pdwMsgAndCertEncodingType,
66  DWORD *pdwContentType, HCERTSTORE *phCertStore, const void **ppvContext)
67 {
68     CERT_BLOB fileBlob;
69     const CERT_BLOB *blob;
70     HCERTSTORE store;
71     DWORD contentType;
72     BOOL ret;
73
74     switch (dwObjectType)
75     {
76     case CERT_QUERY_OBJECT_FILE:
77         /* Cert, CRL, and CTL contexts can't be "embedded" in a file, so
78          * just read the file directly
79          */
80         ret = CRYPT_ReadBlobFromFile((LPCWSTR)pvObject, &fileBlob);
81         blob = &fileBlob;
82         break;
83     case CERT_QUERY_OBJECT_BLOB:
84         blob = (const CERT_BLOB *)pvObject;
85         ret = TRUE;
86         break;
87     default:
88         SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */
89         ret = FALSE;
90     }
91     if (!ret)
92         return FALSE;
93
94     store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
95      CERT_STORE_CREATE_NEW_FLAG, NULL);
96     ret = FALSE;
97     if (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CERT)
98     {
99         ret = pCertInterface->addEncodedToStore(store, X509_ASN_ENCODING,
100          blob->pbData, blob->cbData, CERT_STORE_ADD_ALWAYS, ppvContext);
101         if (ret)
102             contentType = CERT_QUERY_CONTENT_CERT;
103     }
104     if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CRL))
105     {
106         ret = pCRLInterface->addEncodedToStore(store, X509_ASN_ENCODING,
107          blob->pbData, blob->cbData, CERT_STORE_ADD_ALWAYS, ppvContext);
108         if (ret)
109             contentType = CERT_QUERY_CONTENT_CRL;
110     }
111     if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CTL))
112     {
113         ret = pCTLInterface->addEncodedToStore(store, X509_ASN_ENCODING,
114          blob->pbData, blob->cbData, CERT_STORE_ADD_ALWAYS, ppvContext);
115         if (ret)
116             contentType = CERT_QUERY_CONTENT_CTL;
117     }
118     if (ret)
119     {
120         if (pdwMsgAndCertEncodingType)
121             *pdwMsgAndCertEncodingType = X509_ASN_ENCODING;
122         if (pdwContentType)
123             *pdwContentType = contentType;
124         if (phCertStore)
125             *phCertStore = CertDuplicateStore(store);
126     }
127     CertCloseStore(store, 0);
128     if (blob == &fileBlob)
129         CryptMemFree(blob->pbData);
130     TRACE("returning %d\n", ret);
131     return ret;
132 }
133
134 static BOOL CRYPT_QuerySerializedContextObject(DWORD dwObjectType,
135  const void *pvObject, DWORD dwExpectedContentTypeFlags,
136  DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
137  HCERTSTORE *phCertStore, const void **ppvContext)
138 {
139     CERT_BLOB fileBlob;
140     const CERT_BLOB *blob;
141     const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
142     const void *context;
143     DWORD contextType;
144     BOOL ret;
145
146     switch (dwObjectType)
147     {
148     case CERT_QUERY_OBJECT_FILE:
149         /* Cert, CRL, and CTL contexts can't be "embedded" in a file, so
150          * just read the file directly
151          */
152         ret = CRYPT_ReadBlobFromFile((LPCWSTR)pvObject, &fileBlob);
153         blob = &fileBlob;
154         break;
155     case CERT_QUERY_OBJECT_BLOB:
156         blob = (const CERT_BLOB *)pvObject;
157         ret = TRUE;
158         break;
159     default:
160         SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */
161         ret = FALSE;
162     }
163     if (!ret)
164         return FALSE;
165
166     context = CRYPT_ReadSerializedElement(blob->pbData, blob->cbData,
167      CERT_STORE_ALL_CONTEXT_FLAG, &contextType);
168     if (context)
169     {
170         DWORD contentType, certStoreOffset;
171
172         ret = TRUE;
173         switch (contextType)
174         {
175         case CERT_STORE_CERTIFICATE_CONTEXT:
176             contextInterface = pCertInterface;
177             contentType = CERT_QUERY_CONTENT_SERIALIZED_CERT;
178             certStoreOffset = offsetof(CERT_CONTEXT, hCertStore);
179             if (!(dwExpectedContentTypeFlags &
180              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT))
181             {
182                 SetLastError(ERROR_INVALID_DATA);
183                 ret = FALSE;
184                 goto end;
185             }
186             break;
187         case CERT_STORE_CRL_CONTEXT:
188             contextInterface = pCRLInterface;
189             contentType = CERT_QUERY_CONTENT_SERIALIZED_CRL;
190             certStoreOffset = offsetof(CRL_CONTEXT, hCertStore);
191             if (!(dwExpectedContentTypeFlags &
192              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL))
193             {
194                 SetLastError(ERROR_INVALID_DATA);
195                 ret = FALSE;
196                 goto end;
197             }
198             break;
199         case CERT_STORE_CTL_CONTEXT:
200             contextInterface = pCTLInterface;
201             contentType = CERT_QUERY_CONTENT_SERIALIZED_CTL;
202             certStoreOffset = offsetof(CTL_CONTEXT, hCertStore);
203             if (!(dwExpectedContentTypeFlags &
204              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL))
205             {
206                 SetLastError(ERROR_INVALID_DATA);
207                 ret = FALSE;
208                 goto end;
209             }
210             break;
211         default:
212             SetLastError(ERROR_INVALID_DATA);
213             ret = FALSE;
214             goto end;
215         }
216         if (pdwMsgAndCertEncodingType)
217             *pdwMsgAndCertEncodingType = X509_ASN_ENCODING;
218         if (pdwContentType)
219             *pdwContentType = contentType;
220         if (phCertStore)
221             *phCertStore = CertDuplicateStore(
222              *(HCERTSTORE *)((const BYTE *)context + certStoreOffset));
223         if (ppvContext)
224             *ppvContext = contextInterface->duplicate(context);
225     }
226
227 end:
228     if (contextInterface && context)
229         contextInterface->free(context);
230     if (blob == &fileBlob)
231         CryptMemFree(blob->pbData);
232     TRACE("returning %d\n", ret);
233     return ret;
234 }
235
236 static BOOL CRYPT_QuerySerializedStoreObject(DWORD dwObjectType,
237  const void *pvObject, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
238  HCERTSTORE *phCertStore, HCRYPTMSG *phMsg)
239 {
240     LPCWSTR fileName = (LPCWSTR)pvObject;
241     HANDLE file;
242     BOOL ret = FALSE;
243
244     if (dwObjectType != CERT_QUERY_OBJECT_FILE)
245     {
246         FIXME("unimplemented for non-file type %d\n", dwObjectType);
247         SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */
248         return FALSE;
249     }
250     TRACE("%s\n", debugstr_w(fileName));
251     file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
252      OPEN_EXISTING, 0, NULL);
253     if (file != INVALID_HANDLE_VALUE)
254     {
255         HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
256          CERT_STORE_CREATE_NEW_FLAG, NULL);
257
258         ret = CRYPT_ReadSerializedStoreFromFile(file, store);
259         if (ret)
260         {
261             if (pdwMsgAndCertEncodingType)
262                 *pdwMsgAndCertEncodingType = X509_ASN_ENCODING;
263             if (pdwContentType)
264                 *pdwContentType = CERT_QUERY_CONTENT_SERIALIZED_STORE;
265             if (phCertStore)
266                 *phCertStore = CertDuplicateStore(store);
267         }
268         CertCloseStore(store, 0);
269         CloseHandle(file);
270     }
271     TRACE("returning %d\n", ret);
272     return ret;
273 }
274
275 /* Used to decode non-embedded messages */
276 static BOOL CRYPT_QueryMessageObject(DWORD dwObjectType, const void *pvObject,
277  DWORD dwExpectedContentTypeFlags, DWORD *pdwMsgAndCertEncodingType,
278  DWORD *pdwContentType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg)
279 {
280     CERT_BLOB fileBlob;
281     const CERT_BLOB *blob;
282     BOOL ret;
283     HCRYPTMSG msg = NULL;
284     DWORD encodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
285
286     switch (dwObjectType)
287     {
288     case CERT_QUERY_OBJECT_FILE:
289         /* This isn't an embedded PKCS7 message, so just read the file
290          * directly
291          */
292         ret = CRYPT_ReadBlobFromFile((LPCWSTR)pvObject, &fileBlob);
293         blob = &fileBlob;
294         break;
295     case CERT_QUERY_OBJECT_BLOB:
296         blob = (const CERT_BLOB *)pvObject;
297         ret = TRUE;
298         break;
299     default:
300         SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */
301         ret = FALSE;
302     }
303     if (!ret)
304         return FALSE;
305
306     ret = FALSE;
307     /* Try it first as a PKCS content info */
308     if ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED) ||
309      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED))
310     {
311         msg = CryptMsgOpenToDecode(encodingType, 0, 0, 0, NULL, NULL);
312         if (msg)
313         {
314             ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE);
315             if (ret)
316             {
317                 DWORD type, len = sizeof(type);
318
319                 ret = CryptMsgGetParam(msg, CMSG_TYPE_PARAM, 0, &type, &len);
320                 if (ret)
321                 {
322                     if ((dwExpectedContentTypeFlags &
323                      CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED))
324                     {
325                         if (type != CMSG_SIGNED)
326                         {
327                             SetLastError(ERROR_INVALID_DATA);
328                             ret = FALSE;
329                         }
330                         else if (pdwContentType)
331                             *pdwContentType = CERT_QUERY_CONTENT_PKCS7_SIGNED;
332                     }
333                     else if ((dwExpectedContentTypeFlags &
334                      CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED))
335                     {
336                         if (type != CMSG_DATA)
337                         {
338                             SetLastError(ERROR_INVALID_DATA);
339                             ret = FALSE;
340                         }
341                         else if (pdwContentType)
342                             *pdwContentType = CERT_QUERY_CONTENT_PKCS7_UNSIGNED;
343                     }
344                 }
345             }
346             if (!ret)
347             {
348                 CryptMsgClose(msg);
349                 msg = NULL;
350             }
351         }
352     }
353     /* Failing that, try explicitly typed messages */
354     if (!ret &&
355      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED))
356     {
357         msg = CryptMsgOpenToDecode(encodingType, 0, CMSG_SIGNED, 0, NULL, NULL);
358         if (msg)
359         {
360             ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE);
361             if (!ret)
362             {
363                 CryptMsgClose(msg);
364                 msg = NULL;
365             }
366         }
367         if (msg && pdwContentType)
368             *pdwContentType = CERT_QUERY_CONTENT_PKCS7_SIGNED;
369     }
370     if (!ret &&
371      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED))
372     {
373         msg = CryptMsgOpenToDecode(encodingType, 0, CMSG_DATA, 0, NULL, NULL);
374         if (msg)
375         {
376             ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE);
377             if (!ret)
378             {
379                 CryptMsgClose(msg);
380                 msg = NULL;
381             }
382         }
383         if (msg && pdwContentType)
384             *pdwContentType = CERT_QUERY_CONTENT_PKCS7_UNSIGNED;
385     }
386     if (pdwMsgAndCertEncodingType)
387         *pdwMsgAndCertEncodingType = encodingType;
388     if (msg)
389     {
390         if (phMsg)
391             *phMsg = msg;
392         if (phCertStore)
393             *phCertStore = CertOpenStore(CERT_STORE_PROV_MSG, encodingType, 0,
394              0, msg);
395     }
396     if (blob == &fileBlob)
397         CryptMemFree(blob->pbData);
398     TRACE("returning %d\n", ret);
399     return ret;
400 }
401
402 static BOOL CRYPT_QueryEmbeddedMessageObject(DWORD dwObjectType,
403  const void *pvObject, DWORD dwExpectedContentTypeFlags,
404  DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
405  HCERTSTORE *phCertStore, HCRYPTMSG *phMsg)
406 {
407     HANDLE file;
408     GUID subject;
409     BOOL ret = FALSE;
410
411     TRACE("%s\n", debugstr_w((LPCWSTR)pvObject));
412
413     if (dwObjectType != CERT_QUERY_OBJECT_FILE)
414     {
415         FIXME("don't know what to do for type %d embedded signed messages\n",
416          dwObjectType);
417         SetLastError(E_INVALIDARG);
418         return FALSE;
419     }
420     file = CreateFileW((LPCWSTR)pvObject, GENERIC_READ, FILE_SHARE_READ,
421      NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
422     if (file != INVALID_HANDLE_VALUE)
423     {
424         ret = CryptSIPRetrieveSubjectGuid((LPCWSTR)pvObject, file, &subject);
425         if (ret)
426         {
427             SIP_DISPATCH_INFO sip;
428
429             memset(&sip, 0, sizeof(sip));
430             sip.cbSize = sizeof(sip);
431             ret = CryptSIPLoad(&subject, 0, &sip);
432             if (ret)
433             {
434                 SIP_SUBJECTINFO subjectInfo;
435                 CERT_BLOB blob;
436                 DWORD encodingType;
437
438                 memset(&subjectInfo, 0, sizeof(subjectInfo));
439                 subjectInfo.cbSize = sizeof(subjectInfo);
440                 subjectInfo.pgSubjectType = &subject;
441                 subjectInfo.hFile = file;
442                 subjectInfo.pwsFileName = (LPCWSTR)pvObject;
443                 ret = sip.pfGet(&subjectInfo, &encodingType, 0, &blob.cbData,
444                  NULL);
445                 if (ret)
446                 {
447                     blob.pbData = CryptMemAlloc(blob.cbData);
448                     if (blob.pbData)
449                     {
450                         ret = sip.pfGet(&subjectInfo, &encodingType, 0,
451                          &blob.cbData, blob.pbData);
452                         if (ret)
453                         {
454                             ret = CRYPT_QueryMessageObject(
455                              CERT_QUERY_OBJECT_BLOB, &blob,
456                              CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
457                              pdwMsgAndCertEncodingType, NULL, phCertStore,
458                              phMsg);
459                             if (ret && pdwContentType)
460                                 *pdwContentType =
461                                  CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED;
462                         }
463                         CryptMemFree(blob.pbData);
464                     }
465                     else
466                     {
467                         SetLastError(ERROR_OUTOFMEMORY);
468                         ret = FALSE;
469                     }
470                 }
471             }
472         }
473         CloseHandle(file);
474     }
475     TRACE("returning %d\n", ret);
476     return ret;
477 }
478
479 BOOL WINAPI CryptQueryObject(DWORD dwObjectType, const void *pvObject,
480  DWORD dwExpectedContentTypeFlags, DWORD dwExpectedFormatTypeFlags,
481  DWORD dwFlags, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
482  DWORD *pdwFormatType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg,
483  const void **ppvContext)
484 {
485     static const DWORD unimplementedTypes =
486      CERT_QUERY_CONTENT_FLAG_PKCS10 | CERT_QUERY_CONTENT_FLAG_PFX |
487      CERT_QUERY_CONTENT_FLAG_CERT_PAIR;
488     BOOL ret = TRUE;
489
490     TRACE("(%08x, %p, %08x, %08x, %08x, %p, %p, %p, %p, %p, %p)\n",
491      dwObjectType, pvObject, dwExpectedContentTypeFlags,
492      dwExpectedFormatTypeFlags, dwFlags, pdwMsgAndCertEncodingType,
493      pdwContentType, pdwFormatType, phCertStore, phMsg, ppvContext);
494
495     if (dwExpectedContentTypeFlags & unimplementedTypes)
496         WARN("unimplemented for types %08x\n",
497          dwExpectedContentTypeFlags & unimplementedTypes);
498     if (!(dwExpectedFormatTypeFlags & CERT_QUERY_FORMAT_FLAG_BINARY))
499     {
500         FIXME("unimplemented for anything but binary\n");
501         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
502         return FALSE;
503     }
504     if (pdwFormatType)
505         *pdwFormatType = CERT_QUERY_FORMAT_BINARY;
506
507     if (phCertStore)
508         *phCertStore = NULL;
509     if (phMsg)
510         *phMsg = NULL;
511     if (ppvContext)
512         *ppvContext = NULL;
513
514     ret = FALSE;
515     if ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CERT) ||
516      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CRL) ||
517      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CTL))
518     {
519         ret = CRYPT_QueryContextObject(dwObjectType, pvObject,
520          dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType,
521          phCertStore, ppvContext);
522     }
523     if (!ret &&
524      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE))
525     {
526         ret = CRYPT_QuerySerializedStoreObject(dwObjectType, pvObject,
527          pdwMsgAndCertEncodingType, pdwContentType, phCertStore, phMsg);
528     }
529     if (!ret &&
530      ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT) ||
531      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL) ||
532      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL)))
533     {
534         ret = CRYPT_QuerySerializedContextObject(dwObjectType, pvObject,
535          dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType,
536          phCertStore, ppvContext);
537     }
538     if (!ret &&
539      ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED) ||
540      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED)))
541     {
542         ret = CRYPT_QueryMessageObject(dwObjectType, pvObject,
543          dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType,
544          phCertStore, phMsg);
545     }
546     if (!ret &&
547      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED))
548     {
549         ret = CRYPT_QueryEmbeddedMessageObject(dwObjectType, pvObject,
550          dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType,
551          phCertStore, phMsg);
552     }
553     TRACE("returning %d\n", ret);
554     return ret;
555 }
556
557 static BOOL WINAPI CRYPT_FormatHexString(DWORD dwCertEncodingType,
558  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
559  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
560  DWORD *pcbFormat)
561 {
562     BOOL ret;
563     DWORD bytesNeeded;
564
565     if (cbEncoded)
566         bytesNeeded = (cbEncoded * 3) * sizeof(WCHAR);
567     else
568         bytesNeeded = sizeof(WCHAR);
569     if (!pbFormat)
570     {
571         *pcbFormat = bytesNeeded;
572         ret = TRUE;
573     }
574     else if (*pcbFormat < bytesNeeded)
575     {
576         *pcbFormat = bytesNeeded;
577         SetLastError(ERROR_MORE_DATA);
578         ret = FALSE;
579     }
580     else
581     {
582         static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
583         static const WCHAR endFmt[] = { '%','0','2','x',0 };
584         DWORD i;
585         LPWSTR ptr = pbFormat;
586
587         *pcbFormat = bytesNeeded;
588         if (cbEncoded)
589         {
590             for (i = 0; i < cbEncoded; i++)
591             {
592                 if (i < cbEncoded - 1)
593                     ptr += sprintfW(ptr, fmt, pbEncoded[i]);
594                 else
595                     ptr += sprintfW(ptr, endFmt, pbEncoded[i]);
596             }
597         }
598         else
599             *ptr = 0;
600         ret = TRUE;
601     }
602     return ret;
603 }
604
605 #define MAX_STRING_RESOURCE_LEN 128
606
607 static const WCHAR crlf[] = { '\r','\n',0 };
608 static const WCHAR commaSpace[] = { ',',' ',0 };
609
610 static WCHAR subjectTypeHeader[MAX_STRING_RESOURCE_LEN];
611 static WCHAR subjectTypeCA[MAX_STRING_RESOURCE_LEN];
612 static WCHAR subjectTypeEndCert[MAX_STRING_RESOURCE_LEN];
613 static WCHAR pathLengthHeader[MAX_STRING_RESOURCE_LEN];
614
615 static BOOL WINAPI CRYPT_FormatBasicConstraints2(DWORD dwCertEncodingType,
616  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
617  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
618  DWORD *pcbFormat)
619 {
620     DWORD size;
621     CERT_BASIC_CONSTRAINTS2_INFO *info;
622     BOOL ret;
623
624     if (!cbEncoded)
625     {
626         SetLastError(E_INVALIDARG);
627         return FALSE;
628     }
629     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_BASIC_CONSTRAINTS2,
630      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size)))
631     {
632         static const WCHAR pathFmt[] = { '%','d',0 };
633         static BOOL stringsLoaded = FALSE;
634         DWORD bytesNeeded = sizeof(WCHAR); /* space for the NULL terminator */
635         WCHAR pathLength[MAX_STRING_RESOURCE_LEN];
636         LPCWSTR sep, subjectType;
637         DWORD sepLen;
638
639         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
640         {
641             sep = crlf;
642             sepLen = strlenW(crlf) * sizeof(WCHAR);
643         }
644         else
645         {
646             sep = commaSpace;
647             sepLen = strlenW(commaSpace) * sizeof(WCHAR);
648         }
649
650         if (!stringsLoaded)
651         {
652             LoadStringW(hInstance, IDS_SUBJECT_TYPE, subjectTypeHeader,
653              sizeof(subjectTypeHeader) / sizeof(subjectTypeHeader[0]));
654             LoadStringW(hInstance, IDS_SUBJECT_TYPE_CA, subjectTypeCA,
655              sizeof(subjectTypeCA) / sizeof(subjectTypeCA[0]));
656             LoadStringW(hInstance, IDS_SUBJECT_TYPE_END_CERT,
657              subjectTypeEndCert,
658              sizeof(subjectTypeEndCert) / sizeof(subjectTypeEndCert[0]));
659             LoadStringW(hInstance, IDS_PATH_LENGTH, pathLengthHeader,
660              sizeof(pathLengthHeader) / sizeof(pathLengthHeader[0]));
661             stringsLoaded = TRUE;
662         }
663         bytesNeeded += strlenW(subjectTypeHeader) * sizeof(WCHAR);
664         if (info->fCA)
665             subjectType = subjectTypeCA;
666         else
667             subjectType = subjectTypeEndCert;
668         bytesNeeded += strlenW(subjectType) * sizeof(WCHAR);
669         bytesNeeded += sepLen;
670         bytesNeeded += strlenW(pathLengthHeader) * sizeof(WCHAR);
671         if (info->fPathLenConstraint)
672             sprintfW(pathLength, pathFmt, info->dwPathLenConstraint);
673         else
674             LoadStringW(hInstance, IDS_PATH_LENGTH_NONE, pathLength,
675              sizeof(pathLength) / sizeof(pathLength[0]));
676         bytesNeeded += strlenW(pathLength) * sizeof(WCHAR);
677         if (!pbFormat)
678             *pcbFormat = bytesNeeded;
679         else if (*pcbFormat < bytesNeeded)
680         {
681             *pcbFormat = bytesNeeded;
682             SetLastError(ERROR_MORE_DATA);
683             ret = FALSE;
684         }
685         else
686         {
687             LPWSTR str = pbFormat;
688
689             *pcbFormat = bytesNeeded;
690             strcpyW(str, subjectTypeHeader);
691             str += strlenW(subjectTypeHeader);
692             strcpyW(str, subjectType);
693             str += strlenW(subjectType);
694             strcpyW(str, sep);
695             str += sepLen / sizeof(WCHAR);
696             strcpyW(str, pathLengthHeader);
697             str += strlenW(pathLengthHeader);
698             strcpyW(str, pathLength);
699             str += strlenW(pathLength);
700         }
701         LocalFree(info);
702     }
703     return ret;
704 }
705
706 static BOOL CRYPT_FormatHexStringWithPrefix(CRYPT_DATA_BLOB *blob, int id,
707  LPWSTR str, DWORD *pcbStr)
708 {
709     WCHAR buf[MAX_STRING_RESOURCE_LEN];
710     DWORD bytesNeeded;
711     BOOL ret;
712
713     LoadStringW(hInstance, id, buf, sizeof(buf) / sizeof(buf[0]));
714     CRYPT_FormatHexString(X509_ASN_ENCODING, 0, 0, NULL, NULL,
715      blob->pbData, blob->cbData, NULL, &bytesNeeded);
716     bytesNeeded += strlenW(buf) * sizeof(WCHAR);
717     if (!str)
718     {
719         *pcbStr = bytesNeeded;
720         ret = TRUE;
721     }
722     else if (*pcbStr < bytesNeeded)
723     {
724         *pcbStr = bytesNeeded;
725         SetLastError(ERROR_MORE_DATA);
726         ret = FALSE;
727     }
728     else
729     {
730         *pcbStr = bytesNeeded;
731         strcpyW(str, buf);
732         str += strlenW(str);
733         bytesNeeded -= strlenW(str) * sizeof(WCHAR);
734         ret = CRYPT_FormatHexString(X509_ASN_ENCODING, 0, 0, NULL, NULL,
735          blob->pbData, blob->cbData, str, &bytesNeeded);
736     }
737     return ret;
738 }
739
740 static BOOL CRYPT_FormatKeyId(CRYPT_DATA_BLOB *keyId, LPWSTR str,
741  DWORD *pcbStr)
742 {
743     return CRYPT_FormatHexStringWithPrefix(keyId, IDS_KEY_ID, str, pcbStr);
744 }
745
746 static BOOL CRYPT_FormatCertSerialNumber(CRYPT_DATA_BLOB *serialNum, LPWSTR str,
747  DWORD *pcbStr)
748 {
749     return CRYPT_FormatHexStringWithPrefix(serialNum, IDS_CERT_SERIAL_NUMBER,
750      str, pcbStr);
751 }
752
753 static const WCHAR indent[] = { ' ',' ',' ',' ',' ',0 };
754
755 static BOOL CRYPT_FormatAltNameEntry(DWORD dwFormatStrType, DWORD indentLevel,
756  CERT_ALT_NAME_ENTRY *entry, LPWSTR str, DWORD *pcbStr)
757 {
758     BOOL ret;
759     WCHAR buf[MAX_STRING_RESOURCE_LEN];
760     WCHAR mask[MAX_STRING_RESOURCE_LEN];
761     WCHAR ipAddrBuf[32];
762     WCHAR maskBuf[16];
763     DWORD bytesNeeded = sizeof(WCHAR);
764
765     if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
766         bytesNeeded += indentLevel * strlenW(indent) * sizeof(WCHAR);
767     switch (entry->dwAltNameChoice)
768     {
769     case CERT_ALT_NAME_RFC822_NAME:
770         LoadStringW(hInstance, IDS_ALT_NAME_RFC822_NAME, buf,
771          sizeof(buf) / sizeof(buf[0]));
772         bytesNeeded += strlenW(entry->u.pwszRfc822Name) * sizeof(WCHAR);
773         ret = TRUE;
774         break;
775     case CERT_ALT_NAME_DNS_NAME:
776         LoadStringW(hInstance, IDS_ALT_NAME_DNS_NAME, buf,
777          sizeof(buf) / sizeof(buf[0]));
778         bytesNeeded += strlenW(entry->u.pwszDNSName) * sizeof(WCHAR);
779         ret = TRUE;
780         break;
781     case CERT_ALT_NAME_DIRECTORY_NAME:
782     {
783         DWORD strType = dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE
784          ? CERT_NAME_STR_CRLF_FLAG : 0;
785         DWORD directoryNameLen = CertNameToStrW(X509_ASN_ENCODING,
786          &entry->u.DirectoryName, strType, NULL, 0);
787
788         LoadStringW(hInstance, IDS_ALT_NAME_OTHER_NAME, buf,
789          sizeof(buf) / sizeof(buf[0]));
790         bytesNeeded += (directoryNameLen - 1) * sizeof(WCHAR);
791         ret = TRUE;
792         break;
793     }
794     case CERT_ALT_NAME_URL:
795         LoadStringW(hInstance, IDS_ALT_NAME_URL, buf,
796          sizeof(buf) / sizeof(buf[0]));
797         bytesNeeded += strlenW(entry->u.pwszURL) * sizeof(WCHAR);
798         ret = TRUE;
799         break;
800     case CERT_ALT_NAME_IP_ADDRESS:
801     {
802         static const WCHAR ipAddrWithMaskFmt[] = { '%','d','.','%','d','.',
803          '%','d','.','%','d','/','%','d','.','%','d','.','%','d','.','%','d',0
804         };
805         static const WCHAR ipAddrFmt[] = { '%','d','.','%','d','.','%','d',
806          '.','%','d',0 };
807
808         LoadStringW(hInstance, IDS_ALT_NAME_IP_ADDRESS, buf,
809          sizeof(buf) / sizeof(buf[0]));
810         if (entry->u.IPAddress.cbData == 8)
811         {
812             if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
813             {
814                 LoadStringW(hInstance, IDS_ALT_NAME_MASK, mask,
815                  sizeof(mask) / sizeof(mask[0]));
816                 bytesNeeded += strlenW(mask) * sizeof(WCHAR);
817                 sprintfW(ipAddrBuf, ipAddrFmt,
818                  entry->u.IPAddress.pbData[0],
819                  entry->u.IPAddress.pbData[1],
820                  entry->u.IPAddress.pbData[2],
821                  entry->u.IPAddress.pbData[3]);
822                 bytesNeeded += strlenW(ipAddrBuf) * sizeof(WCHAR);
823                 /* indent again, for the mask line */
824                 bytesNeeded += indentLevel * strlenW(indent) * sizeof(WCHAR);
825                 sprintfW(maskBuf, ipAddrFmt,
826                  entry->u.IPAddress.pbData[4],
827                  entry->u.IPAddress.pbData[5],
828                  entry->u.IPAddress.pbData[6],
829                  entry->u.IPAddress.pbData[7]);
830                 bytesNeeded += strlenW(maskBuf) * sizeof(WCHAR);
831                 bytesNeeded += strlenW(crlf) * sizeof(WCHAR);
832             }
833             else
834             {
835                 sprintfW(ipAddrBuf, ipAddrWithMaskFmt,
836                  entry->u.IPAddress.pbData[0],
837                  entry->u.IPAddress.pbData[1],
838                  entry->u.IPAddress.pbData[2],
839                  entry->u.IPAddress.pbData[3],
840                  entry->u.IPAddress.pbData[4],
841                  entry->u.IPAddress.pbData[5],
842                  entry->u.IPAddress.pbData[6],
843                  entry->u.IPAddress.pbData[7]);
844                 bytesNeeded += (strlenW(ipAddrBuf) + 1) * sizeof(WCHAR);
845             }
846             ret = TRUE;
847         }
848         else
849         {
850             FIXME("unknown IP address format (%d bytes)\n",
851              entry->u.IPAddress.cbData);
852             ret = FALSE;
853         }
854         break;
855     }
856     default:
857         FIXME("unimplemented for %d\n", entry->dwAltNameChoice);
858         ret = FALSE;
859     }
860     if (ret)
861     {
862         bytesNeeded += strlenW(buf) * sizeof(WCHAR);
863         if (!str)
864             *pcbStr = bytesNeeded;
865         else if (*pcbStr < bytesNeeded)
866         {
867             *pcbStr = bytesNeeded;
868             SetLastError(ERROR_MORE_DATA);
869             ret = FALSE;
870         }
871         else
872         {
873             DWORD i;
874
875             *pcbStr = bytesNeeded;
876             if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
877             {
878                 for (i = 0; i < indentLevel; i++)
879                 {
880                     strcpyW(str, indent);
881                     str += strlenW(indent);
882                 }
883             }
884             strcpyW(str, buf);
885             str += strlenW(str);
886             switch (entry->dwAltNameChoice)
887             {
888             case CERT_ALT_NAME_RFC822_NAME:
889             case CERT_ALT_NAME_DNS_NAME:
890             case CERT_ALT_NAME_URL:
891                 strcpyW(str, entry->u.pwszURL);
892                 break;
893             case CERT_ALT_NAME_DIRECTORY_NAME:
894             {
895                 DWORD strType = dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE
896                  ? CERT_NAME_STR_CRLF_FLAG : 0;
897
898                 CertNameToStrW(X509_ASN_ENCODING, &entry->u.DirectoryName,
899                  strType, str, bytesNeeded / sizeof(WCHAR));
900                 break;
901             }
902             case CERT_ALT_NAME_IP_ADDRESS:
903                 if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
904                 {
905                     strcpyW(str, ipAddrBuf);
906                     str += strlenW(ipAddrBuf);
907                     strcpyW(str, crlf);
908                     str += strlenW(crlf);
909                     if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
910                     {
911                         for (i = 0; i < indentLevel; i++)
912                         {
913                             strcpyW(str, indent);
914                             str += strlenW(indent);
915                         }
916                     }
917                     strcpyW(str, mask);
918                     str += strlenW(mask);
919                     strcpyW(str, maskBuf);
920                 }
921                 else
922                     strcpyW(str, ipAddrBuf);
923                 break;
924             }
925         }
926     }
927     return ret;
928 }
929
930 static BOOL CRYPT_FormatAltNameInfo(DWORD dwFormatStrType, DWORD indentLevel,
931  CERT_ALT_NAME_INFO *name, LPWSTR str, DWORD *pcbStr)
932 {
933     DWORD i, size, bytesNeeded = 0;
934     BOOL ret = TRUE;
935     LPCWSTR sep;
936     DWORD sepLen;
937
938     if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
939     {
940         sep = crlf;
941         sepLen = strlenW(crlf) * sizeof(WCHAR);
942     }
943     else
944     {
945         sep = commaSpace;
946         sepLen = strlenW(commaSpace) * sizeof(WCHAR);
947     }
948
949     for (i = 0; ret && i < name->cAltEntry; i++)
950     {
951         ret = CRYPT_FormatAltNameEntry(dwFormatStrType, indentLevel,
952          &name->rgAltEntry[i], NULL, &size);
953         if (ret)
954         {
955             bytesNeeded += size - sizeof(WCHAR);
956             if (i < name->cAltEntry - 1)
957                 bytesNeeded += sepLen;
958         }
959     }
960     if (ret)
961     {
962         bytesNeeded += sizeof(WCHAR);
963         if (!str)
964             *pcbStr = bytesNeeded;
965         else if (*pcbStr < bytesNeeded)
966         {
967             *pcbStr = bytesNeeded;
968             SetLastError(ERROR_MORE_DATA);
969             ret = FALSE;
970         }
971         else
972         {
973             *pcbStr = bytesNeeded;
974             for (i = 0; ret && i < name->cAltEntry; i++)
975             {
976                 ret = CRYPT_FormatAltNameEntry(dwFormatStrType, indentLevel,
977                  &name->rgAltEntry[i], str, &size);
978                 if (ret)
979                 {
980                     str += size / sizeof(WCHAR) - 1;
981                     if (i < name->cAltEntry - 1)
982                     {
983                         strcpyW(str, sep);
984                         str += sepLen / sizeof(WCHAR);
985                     }
986                 }
987             }
988         }
989     }
990     return ret;
991 }
992
993 static BOOL CRYPT_FormatCertIssuer(DWORD dwFormatStrType,
994  CERT_ALT_NAME_INFO *issuer, LPWSTR str, DWORD *pcbStr)
995 {
996     WCHAR buf[MAX_STRING_RESOURCE_LEN];
997     DWORD bytesNeeded;
998     BOOL ret;
999
1000     LoadStringW(hInstance, IDS_CERT_ISSUER, buf, sizeof(buf) / sizeof(buf[0]));
1001     ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 0, issuer, NULL,
1002      &bytesNeeded);
1003     bytesNeeded += strlenW(buf) * sizeof(WCHAR);
1004     if (ret)
1005     {
1006         if (!str)
1007             *pcbStr = bytesNeeded;
1008         else if (*pcbStr < bytesNeeded)
1009         {
1010             *pcbStr = bytesNeeded;
1011             SetLastError(ERROR_MORE_DATA);
1012             ret = FALSE;
1013         }
1014         else
1015         {
1016             *pcbStr = bytesNeeded;
1017             strcpyW(str, buf);
1018             str += strlenW(str);
1019             bytesNeeded -= strlenW(str) * sizeof(WCHAR);
1020             ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 0, issuer, str,
1021              &bytesNeeded);
1022         }
1023     }
1024     return ret;
1025 }
1026
1027 static BOOL WINAPI CRYPT_FormatAuthorityKeyId2(DWORD dwCertEncodingType,
1028  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
1029  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
1030  DWORD *pcbFormat)
1031 {
1032     CERT_AUTHORITY_KEY_ID2_INFO *info;
1033     DWORD size;
1034     BOOL ret = FALSE;
1035
1036     if (!cbEncoded)
1037     {
1038         SetLastError(E_INVALIDARG);
1039         return FALSE;
1040     }
1041     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_AUTHORITY_KEY_ID2,
1042      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size)))
1043     {
1044         DWORD bytesNeeded = sizeof(WCHAR); /* space for the NULL terminator */
1045         LPCWSTR sep;
1046         DWORD sepLen;
1047         BOOL needSeparator = FALSE;
1048
1049         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1050         {
1051             sep = crlf;
1052             sepLen = strlenW(crlf) * sizeof(WCHAR);
1053         }
1054         else
1055         {
1056             sep = commaSpace;
1057             sepLen = strlenW(commaSpace) * sizeof(WCHAR);
1058         }
1059
1060         if (info->KeyId.cbData)
1061         {
1062             needSeparator = TRUE;
1063             ret = CRYPT_FormatKeyId(&info->KeyId, NULL, &size);
1064             if (ret)
1065             {
1066                 /* don't include NULL-terminator more than once */
1067                 bytesNeeded += size - sizeof(WCHAR);
1068             }
1069         }
1070         if (info->AuthorityCertIssuer.cAltEntry)
1071         {
1072             if (needSeparator)
1073                 bytesNeeded += sepLen;
1074             needSeparator = TRUE;
1075             ret = CRYPT_FormatCertIssuer(dwFormatStrType,
1076              &info->AuthorityCertIssuer, NULL, &size);
1077             if (ret)
1078             {
1079                 /* don't include NULL-terminator more than once */
1080                 bytesNeeded += size - sizeof(WCHAR);
1081             }
1082         }
1083         if (info->AuthorityCertSerialNumber.cbData)
1084         {
1085             if (needSeparator)
1086                 bytesNeeded += sepLen;
1087             ret = CRYPT_FormatCertSerialNumber(
1088              &info->AuthorityCertSerialNumber, NULL, &size);
1089             if (ret)
1090             {
1091                 /* don't include NULL-terminator more than once */
1092                 bytesNeeded += size - sizeof(WCHAR);
1093             }
1094         }
1095         if (ret)
1096         {
1097             if (!pbFormat)
1098                 *pcbFormat = bytesNeeded;
1099             else if (*pcbFormat < bytesNeeded)
1100             {
1101                 *pcbFormat = bytesNeeded;
1102                 SetLastError(ERROR_MORE_DATA);
1103                 ret = FALSE;
1104             }
1105             else
1106             {
1107                 LPWSTR str = pbFormat;
1108
1109                 *pcbFormat = bytesNeeded;
1110                 needSeparator = FALSE;
1111                 if (info->KeyId.cbData)
1112                 {
1113                     needSeparator = TRUE;
1114                     /* Overestimate size available, it's already been checked
1115                      * above.
1116                      */
1117                     size = bytesNeeded;
1118                     ret = CRYPT_FormatKeyId(&info->KeyId, str, &size);
1119                     if (ret)
1120                         str += size / sizeof(WCHAR) - 1;
1121                 }
1122                 if (info->AuthorityCertIssuer.cAltEntry)
1123                 {
1124                     if (needSeparator)
1125                     {
1126                         strcpyW(str, sep);
1127                         str += sepLen / sizeof(WCHAR);
1128                     }
1129                     needSeparator = TRUE;
1130                     /* Overestimate size available, it's already been checked
1131                      * above.
1132                      */
1133                     size = bytesNeeded;
1134                     ret = CRYPT_FormatCertIssuer(dwFormatStrType,
1135                      &info->AuthorityCertIssuer, str, &size);
1136                     if (ret)
1137                         str += size / sizeof(WCHAR) - 1;
1138                 }
1139                 if (info->AuthorityCertSerialNumber.cbData)
1140                 {
1141                     if (needSeparator)
1142                     {
1143                         strcpyW(str, sep);
1144                         str += sepLen / sizeof(WCHAR);
1145                     }
1146                     /* Overestimate size available, it's already been checked
1147                      * above.
1148                      */
1149                     size = bytesNeeded;
1150                     ret = CRYPT_FormatCertSerialNumber(
1151                      &info->AuthorityCertSerialNumber, str, &size);
1152                 }
1153             }
1154         }
1155         LocalFree(info);
1156     }
1157     return ret;
1158 }
1159
1160 static const WCHAR colonCrlf[] = { ':','\r','\n',0 };
1161
1162 static WCHAR aia[MAX_STRING_RESOURCE_LEN];
1163 static WCHAR accessMethod[MAX_STRING_RESOURCE_LEN];
1164 static WCHAR ocsp[MAX_STRING_RESOURCE_LEN];
1165 static WCHAR caIssuers[MAX_STRING_RESOURCE_LEN];
1166 static WCHAR unknown[MAX_STRING_RESOURCE_LEN];
1167 static WCHAR accessLocation[MAX_STRING_RESOURCE_LEN];
1168 static BOOL WINAPI CRYPT_FormatAuthorityInfoAccess(DWORD dwCertEncodingType,
1169
1170  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
1171  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
1172  DWORD *pcbFormat)
1173 {
1174     CERT_AUTHORITY_INFO_ACCESS *info;
1175     DWORD size;
1176     BOOL ret = FALSE;
1177
1178     if (!cbEncoded)
1179     {
1180         SetLastError(E_INVALIDARG);
1181         return FALSE;
1182     }
1183     if ((ret = CryptDecodeObjectEx(dwCertEncodingType,
1184      X509_AUTHORITY_INFO_ACCESS, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG,
1185      NULL, &info, &size)))
1186     {
1187         DWORD bytesNeeded = sizeof(WCHAR);
1188
1189         if (!info->cAccDescr)
1190         {
1191             WCHAR infoNotAvailable[MAX_STRING_RESOURCE_LEN];
1192
1193             LoadStringW(hInstance, IDS_INFO_NOT_AVAILABLE, infoNotAvailable,
1194              sizeof(infoNotAvailable) / sizeof(infoNotAvailable[0]));
1195             bytesNeeded += strlenW(infoNotAvailable) * sizeof(WCHAR);
1196             if (!pbFormat)
1197                 *pcbFormat = bytesNeeded;
1198             else if (*pcbFormat < bytesNeeded)
1199             {
1200                 *pcbFormat = bytesNeeded;
1201                 SetLastError(ERROR_MORE_DATA);
1202                 ret = FALSE;
1203             }
1204             else
1205             {
1206                 *pcbFormat = bytesNeeded;
1207                 strcpyW((LPWSTR)pbFormat, infoNotAvailable);
1208             }
1209         }
1210         else
1211         {
1212             static const WCHAR colonSep[] = { ':',' ',0 };
1213             static const WCHAR numFmt[] = { '%','d',0 };
1214             static const WCHAR equal[] = { '=',0 };
1215             static BOOL stringsLoaded = FALSE;
1216             DWORD i;
1217             LPCWSTR headingSep, accessMethodSep, locationSep;
1218             WCHAR accessDescrNum[11];
1219
1220             if (!stringsLoaded)
1221             {
1222                 LoadStringW(hInstance, IDS_AIA, aia,
1223                  sizeof(aia) / sizeof(aia[0]));
1224                 LoadStringW(hInstance, IDS_ACCESS_METHOD, accessMethod,
1225                  sizeof(accessMethod) / sizeof(accessMethod[0]));
1226                 LoadStringW(hInstance, IDS_ACCESS_METHOD_OCSP, ocsp,
1227                  sizeof(ocsp) / sizeof(ocsp[0]));
1228                 LoadStringW(hInstance, IDS_ACCESS_METHOD_CA_ISSUERS, caIssuers,
1229                  sizeof(caIssuers) / sizeof(caIssuers[0]));
1230                 LoadStringW(hInstance, IDS_ACCESS_METHOD_UNKNOWN, unknown,
1231                  sizeof(unknown) / sizeof(unknown[0]));
1232                 LoadStringW(hInstance, IDS_ACCESS_LOCATION, accessLocation,
1233                  sizeof(accessLocation) / sizeof(accessLocation[0]));
1234                 stringsLoaded = TRUE;
1235             }
1236             if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1237             {
1238                 headingSep = crlf;
1239                 accessMethodSep = crlf;
1240                 locationSep = colonCrlf;
1241             }
1242             else
1243             {
1244                 headingSep = colonSep;
1245                 accessMethodSep = commaSpace;
1246                 locationSep = equal;
1247             }
1248
1249             for (i = 0; ret && i < info->cAccDescr; i++)
1250             {
1251                 /* Heading */
1252                 bytesNeeded += sizeof(WCHAR); /* left bracket */
1253                 sprintfW(accessDescrNum, numFmt, i + 1);
1254                 bytesNeeded += strlenW(accessDescrNum) * sizeof(WCHAR);
1255                 bytesNeeded += sizeof(WCHAR); /* right bracket */
1256                 bytesNeeded += strlenW(aia) * sizeof(WCHAR);
1257                 bytesNeeded += strlenW(headingSep) * sizeof(WCHAR);
1258                 /* Access method */
1259                 bytesNeeded += strlenW(accessMethod) * sizeof(WCHAR);
1260                 if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1261                     bytesNeeded += strlenW(indent) * sizeof(WCHAR);
1262                 if (!strcmp(info->rgAccDescr[i].pszAccessMethod,
1263                  szOID_PKIX_OCSP))
1264                     bytesNeeded += strlenW(ocsp) * sizeof(WCHAR);
1265                 else if (!strcmp(info->rgAccDescr[i].pszAccessMethod,
1266                  szOID_PKIX_CA_ISSUERS))
1267                     bytesNeeded += strlenW(caIssuers) * sizeof(caIssuers);
1268                 else
1269                     bytesNeeded += strlenW(unknown) * sizeof(WCHAR);
1270                 bytesNeeded += sizeof(WCHAR); /* space */
1271                 bytesNeeded += sizeof(WCHAR); /* left paren */
1272                 bytesNeeded += strlen(info->rgAccDescr[i].pszAccessMethod)
1273                  * sizeof(WCHAR);
1274                 bytesNeeded += sizeof(WCHAR); /* right paren */
1275                 /* Delimiter between access method and location */
1276                 bytesNeeded += strlenW(accessMethodSep) * sizeof(WCHAR);
1277                 if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1278                     bytesNeeded += strlenW(indent) * sizeof(WCHAR);
1279                 bytesNeeded += strlenW(accessLocation) * sizeof(WCHAR);
1280                 bytesNeeded += strlenW(locationSep) * sizeof(WCHAR);
1281                 ret = CRYPT_FormatAltNameEntry(dwFormatStrType, 2,
1282                  &info->rgAccDescr[i].AccessLocation, NULL, &size);
1283                 if (ret)
1284                     bytesNeeded += size - sizeof(WCHAR);
1285                 /* Need extra delimiter between access method entries */
1286                 if (i < info->cAccDescr - 1)
1287                     bytesNeeded += strlenW(accessMethodSep) * sizeof(WCHAR);
1288             }
1289             if (ret)
1290             {
1291                 if (!pbFormat)
1292                     *pcbFormat = bytesNeeded;
1293                 else if (*pcbFormat < bytesNeeded)
1294                 {
1295                     *pcbFormat = bytesNeeded;
1296                     SetLastError(ERROR_MORE_DATA);
1297                     ret = FALSE;
1298                 }
1299                 else
1300                 {
1301                     LPWSTR str = pbFormat;
1302                     DWORD altNameEntrySize;
1303
1304                     *pcbFormat = bytesNeeded;
1305                     for (i = 0; ret && i < info->cAccDescr; i++)
1306                     {
1307                         LPCSTR oidPtr;
1308
1309                         *str++ = '[';
1310                         sprintfW(accessDescrNum, numFmt, i + 1);
1311                         strcpyW(str, accessDescrNum);
1312                         str += strlenW(accessDescrNum);
1313                         *str++ = ']';
1314                         strcpyW(str, aia);
1315                         str += strlenW(aia);
1316                         strcpyW(str, headingSep);
1317                         str += strlenW(headingSep);
1318                         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1319                         {
1320                             strcpyW(str, indent);
1321                             str += strlenW(indent);
1322                         }
1323                         strcpyW(str, accessMethod);
1324                         str += strlenW(accessMethod);
1325                         if (!strcmp(info->rgAccDescr[i].pszAccessMethod,
1326                          szOID_PKIX_OCSP))
1327                         {
1328                             strcpyW(str, ocsp);
1329                             str += strlenW(ocsp);
1330                         }
1331                         else if (!strcmp(info->rgAccDescr[i].pszAccessMethod,
1332                          szOID_PKIX_CA_ISSUERS))
1333                         {
1334                             strcpyW(str, caIssuers);
1335                             str += strlenW(caIssuers);
1336                         }
1337                         else
1338                         {
1339                             strcpyW(str, unknown);
1340                             str += strlenW(unknown);
1341                         }
1342                         *str++ = ' ';
1343                         *str++ = '(';
1344                         for (oidPtr = info->rgAccDescr[i].pszAccessMethod;
1345                          *oidPtr; oidPtr++, str++)
1346                             *str = *oidPtr;
1347                         *str++ = ')';
1348                         strcpyW(str, accessMethodSep);
1349                         str += strlenW(accessMethodSep);
1350                         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1351                         {
1352                             strcpyW(str, indent);
1353                             str += strlenW(indent);
1354                         }
1355                         strcpyW(str, accessLocation);
1356                         str += strlenW(accessLocation);
1357                         strcpyW(str, locationSep);
1358                         str += strlenW(locationSep);
1359                         /* This overestimates the size available, but that
1360                          * won't matter since we checked earlier whether enough
1361                          * space for the entire string was available.
1362                          */
1363                         altNameEntrySize = bytesNeeded;
1364                         ret = CRYPT_FormatAltNameEntry(dwFormatStrType, 2,
1365                          &info->rgAccDescr[i].AccessLocation, str,
1366                          &altNameEntrySize);
1367                         if (ret)
1368                             str += altNameEntrySize / sizeof(WCHAR) - 1;
1369                         if (i < info->cAccDescr - 1)
1370                         {
1371                             strcpyW(str, accessMethodSep);
1372                             str += strlenW(accessMethodSep);
1373                         }
1374                     }
1375                 }
1376             }
1377         }
1378         LocalFree(info);
1379     }
1380     return ret;
1381 }
1382
1383 static WCHAR keyCompromise[MAX_STRING_RESOURCE_LEN];
1384 static WCHAR caCompromise[MAX_STRING_RESOURCE_LEN];
1385 static WCHAR affiliationChanged[MAX_STRING_RESOURCE_LEN];
1386 static WCHAR superceded[MAX_STRING_RESOURCE_LEN];
1387 static WCHAR operationCeased[MAX_STRING_RESOURCE_LEN];
1388 static WCHAR certificateHold[MAX_STRING_RESOURCE_LEN];
1389
1390 struct reason_map_entry
1391 {
1392     BYTE   reasonBit;
1393     LPWSTR reason;
1394     int    id;
1395 };
1396 static struct reason_map_entry reason_map[] = {
1397  { CRL_REASON_KEY_COMPROMISE_FLAG, keyCompromise, IDS_REASON_KEY_COMPROMISE },
1398  { CRL_REASON_CA_COMPROMISE_FLAG, caCompromise, IDS_REASON_CA_COMPROMISE },
1399  { CRL_REASON_AFFILIATION_CHANGED_FLAG, affiliationChanged,
1400    IDS_REASON_AFFILIATION_CHANGED },
1401  { CRL_REASON_SUPERSEDED_FLAG, superceded, IDS_REASON_SUPERCEDED },
1402  { CRL_REASON_CESSATION_OF_OPERATION_FLAG, operationCeased,
1403    IDS_REASON_CESSATION_OF_OPERATION },
1404  { CRL_REASON_CERTIFICATE_HOLD_FLAG, certificateHold,
1405    IDS_REASON_CERTIFICATE_HOLD },
1406 };
1407
1408 static BOOL CRYPT_FormatReason(DWORD dwFormatStrType,
1409  const CRYPT_BIT_BLOB *reasonFlags, LPWSTR str, DWORD *pcbStr)
1410 {
1411     static const WCHAR sep[] = { ',',' ',0 };
1412     static const WCHAR bitsFmt[] = { ' ','(','%','0','2','x',')',0 };
1413     static BOOL stringsLoaded = FALSE;
1414     int i, numReasons = 0;
1415     BOOL ret = TRUE;
1416     DWORD bytesNeeded = sizeof(WCHAR);
1417     WCHAR bits[6];
1418
1419     if (!stringsLoaded)
1420     {
1421         for (i = 0; i < sizeof(reason_map) / sizeof(reason_map[0]); i++)
1422             LoadStringW(hInstance, reason_map[i].id, reason_map[i].reason,
1423              MAX_STRING_RESOURCE_LEN);
1424         stringsLoaded = TRUE;
1425     }
1426     /* No need to check reasonFlags->cbData, we already know it's positive.
1427      * Ignore any other bytes, as they're for undefined bits.
1428      */
1429     for (i = 0; i < sizeof(reason_map) / sizeof(reason_map[0]); i++)
1430     {
1431         if (reasonFlags->pbData[0] & reason_map[i].reasonBit)
1432         {
1433             bytesNeeded += strlenW(reason_map[i].reason) * sizeof(WCHAR);
1434             if (numReasons++)
1435                 bytesNeeded += strlenW(sep) * sizeof(WCHAR);
1436         }
1437     }
1438     sprintfW(bits, bitsFmt, reasonFlags->pbData[0]);
1439     bytesNeeded += strlenW(bits);
1440     if (!str)
1441         *pcbStr = bytesNeeded;
1442     else if (*pcbStr < bytesNeeded)
1443     {
1444         *pcbStr = bytesNeeded;
1445         SetLastError(ERROR_MORE_DATA);
1446         ret = FALSE;
1447     }
1448     else
1449     {
1450         *pcbStr = bytesNeeded;
1451         for (i = 0; i < sizeof(reason_map) / sizeof(reason_map[0]); i++)
1452         {
1453             if (reasonFlags->pbData[0] & reason_map[i].reasonBit)
1454             {
1455                 strcpyW(str, reason_map[i].reason);
1456                 str += strlenW(reason_map[i].reason);
1457                 if (i < sizeof(reason_map) / sizeof(reason_map[0]) - 1 &&
1458                  numReasons)
1459                 {
1460                     strcpyW(str, sep);
1461                     str += strlenW(sep);
1462                 }
1463             }
1464         }
1465         strcpyW(str, bits);
1466     }
1467     return ret;
1468 }
1469
1470 static WCHAR crlDistPoint[MAX_STRING_RESOURCE_LEN];
1471 static WCHAR distPointName[MAX_STRING_RESOURCE_LEN];
1472 static WCHAR fullName[MAX_STRING_RESOURCE_LEN];
1473 static WCHAR rdnName[MAX_STRING_RESOURCE_LEN];
1474 static WCHAR reason[MAX_STRING_RESOURCE_LEN];
1475 static WCHAR issuer[MAX_STRING_RESOURCE_LEN];
1476
1477 static BOOL WINAPI CRYPT_FormatCRLDistPoints(DWORD dwCertEncodingType,
1478  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
1479  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
1480  DWORD *pcbFormat)
1481 {
1482     CRL_DIST_POINTS_INFO *info;
1483     DWORD size;
1484     BOOL ret = FALSE;
1485
1486     if (!cbEncoded)
1487     {
1488         SetLastError(E_INVALIDARG);
1489         return FALSE;
1490     }
1491     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CRL_DIST_POINTS,
1492      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size)))
1493     {
1494         static const WCHAR numFmt[] = { '%','d',0 };
1495         static const WCHAR colonSep[] = { ':',' ',0 };
1496         static const WCHAR commaSep[] = { ',',' ',0 };
1497         static const WCHAR colon[] = { ':',0 };
1498         static BOOL stringsLoaded = FALSE;
1499         DWORD bytesNeeded = sizeof(WCHAR); /* space for NULL terminator */
1500         BOOL haveAnEntry = FALSE;
1501         LPCWSTR headingSep, distPointSep, nameSep;
1502         WCHAR distPointNum[11];
1503         DWORD i;
1504
1505         if (!stringsLoaded)
1506         {
1507             LoadStringW(hInstance, IDS_CRL_DIST_POINT, crlDistPoint,
1508              sizeof(crlDistPoint) / sizeof(crlDistPoint[0]));
1509             LoadStringW(hInstance, IDS_CRL_DIST_POINT_NAME, distPointName,
1510              sizeof(distPointName) / sizeof(distPointName[0]));
1511             LoadStringW(hInstance, IDS_CRL_DIST_POINT_FULL_NAME, fullName,
1512              sizeof(fullName) / sizeof(fullName[0]));
1513             LoadStringW(hInstance, IDS_CRL_DIST_POINT_RDN_NAME, rdnName,
1514              sizeof(rdnName) / sizeof(rdnName[0]));
1515             LoadStringW(hInstance, IDS_CRL_DIST_POINT_REASON, reason,
1516              sizeof(reason) / sizeof(reason[0]));
1517             LoadStringW(hInstance, IDS_CRL_DIST_POINT_ISSUER, issuer,
1518              sizeof(issuer) / sizeof(issuer[0]));
1519             stringsLoaded = TRUE;
1520         }
1521         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1522         {
1523             headingSep = crlf;
1524             distPointSep = crlf;
1525             nameSep = colonCrlf;
1526         }
1527         else
1528         {
1529             headingSep = colonSep;
1530             distPointSep = commaSep;
1531             nameSep = colon;
1532         }
1533
1534         for (i = 0; ret && i < info->cDistPoint; i++)
1535         {
1536             CRL_DIST_POINT *distPoint = &info->rgDistPoint[i];
1537
1538             if (distPoint->DistPointName.dwDistPointNameChoice !=
1539              CRL_DIST_POINT_NO_NAME)
1540             {
1541                 bytesNeeded += strlenW(distPointName) * sizeof(WCHAR);
1542                 bytesNeeded += strlenW(nameSep) * sizeof(WCHAR);
1543                 if (distPoint->DistPointName.dwDistPointNameChoice ==
1544                  CRL_DIST_POINT_FULL_NAME)
1545                     bytesNeeded += strlenW(fullName) * sizeof(WCHAR);
1546                 else
1547                     bytesNeeded += strlenW(rdnName) * sizeof(WCHAR);
1548                 bytesNeeded += strlenW(nameSep) * sizeof(WCHAR);
1549                 if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1550                     bytesNeeded += 2 * strlenW(indent) * sizeof(WCHAR);
1551                 /* The indent level (3) is higher than when used as the issuer,
1552                  * because the name is subordinate to the name type (full vs.
1553                  * RDN.)
1554                  */
1555                 ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 3,
1556                  &distPoint->DistPointName.u.FullName, NULL, &size);
1557                 if (ret)
1558                     bytesNeeded += size - sizeof(WCHAR);
1559                 haveAnEntry = TRUE;
1560             }
1561             else if (distPoint->ReasonFlags.cbData)
1562             {
1563                 bytesNeeded += strlenW(reason) * sizeof(WCHAR);
1564                 ret = CRYPT_FormatReason(dwFormatStrType,
1565                  &distPoint->ReasonFlags, NULL, &size);
1566                 if (ret)
1567                     bytesNeeded += size - sizeof(WCHAR);
1568                 haveAnEntry = TRUE;
1569             }
1570             else if (distPoint->CRLIssuer.cAltEntry)
1571             {
1572                 bytesNeeded += strlenW(issuer) * sizeof(WCHAR);
1573                 bytesNeeded += strlenW(nameSep) * sizeof(WCHAR);
1574                 ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 2,
1575                  &distPoint->CRLIssuer, NULL, &size);
1576                 if (ret)
1577                     bytesNeeded += size - sizeof(WCHAR);
1578                 haveAnEntry = TRUE;
1579             }
1580             if (haveAnEntry)
1581             {
1582                 bytesNeeded += sizeof(WCHAR); /* left bracket */
1583                 sprintfW(distPointNum, numFmt, i + 1);
1584                 bytesNeeded += strlenW(distPointNum) * sizeof(WCHAR);
1585                 bytesNeeded += sizeof(WCHAR); /* right bracket */
1586                 bytesNeeded += strlenW(crlDistPoint) * sizeof(WCHAR);
1587                 bytesNeeded += strlenW(headingSep) * sizeof(WCHAR);
1588                 if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1589                     bytesNeeded += strlenW(indent) * sizeof(WCHAR);
1590             }
1591         }
1592         if (!haveAnEntry)
1593         {
1594             WCHAR infoNotAvailable[MAX_STRING_RESOURCE_LEN];
1595
1596             LoadStringW(hInstance, IDS_INFO_NOT_AVAILABLE, infoNotAvailable,
1597              sizeof(infoNotAvailable) / sizeof(infoNotAvailable[0]));
1598             bytesNeeded += strlenW(infoNotAvailable) * sizeof(WCHAR);
1599             if (!pbFormat)
1600                 *pcbFormat = bytesNeeded;
1601             else if (*pcbFormat < bytesNeeded)
1602             {
1603                 *pcbFormat = bytesNeeded;
1604                 SetLastError(ERROR_MORE_DATA);
1605                 ret = FALSE;
1606             }
1607             else
1608             {
1609                 *pcbFormat = bytesNeeded;
1610                 strcpyW((LPWSTR)pbFormat, infoNotAvailable);
1611             }
1612         }
1613         else
1614         {
1615             if (!pbFormat)
1616                 *pcbFormat = bytesNeeded;
1617             else if (*pcbFormat < bytesNeeded)
1618             {
1619                 *pcbFormat = bytesNeeded;
1620                 SetLastError(ERROR_MORE_DATA);
1621                 ret = FALSE;
1622             }
1623             else
1624             {
1625                 LPWSTR str = pbFormat;
1626
1627                 *pcbFormat = bytesNeeded;
1628                 for (i = 0; ret && i < info->cDistPoint; i++)
1629                 {
1630                     CRL_DIST_POINT *distPoint = &info->rgDistPoint[i];
1631
1632                     *str++ = '[';
1633                     sprintfW(distPointNum, numFmt, i + 1);
1634                     strcpyW(str, distPointNum);
1635                     str += strlenW(distPointNum);
1636                     *str++ = ']';
1637                     strcpyW(str, crlDistPoint);
1638                     str += strlenW(crlDistPoint);
1639                     strcpyW(str, headingSep);
1640                     str += strlenW(headingSep);
1641                     if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1642                     {
1643                         strcpyW(str, indent);
1644                         str += strlenW(indent);
1645                     }
1646                     if (distPoint->DistPointName.dwDistPointNameChoice !=
1647                      CRL_DIST_POINT_NO_NAME)
1648                     {
1649                         DWORD altNameSize = bytesNeeded;
1650
1651                         strcpyW(str, distPointName);
1652                         str += strlenW(distPointName);
1653                         strcpyW(str, nameSep);
1654                         str += strlenW(nameSep);
1655                         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1656                         {
1657                             strcpyW(str, indent);
1658                             str += strlenW(indent);
1659                             strcpyW(str, indent);
1660                             str += strlenW(indent);
1661                         }
1662                         if (distPoint->DistPointName.dwDistPointNameChoice ==
1663                          CRL_DIST_POINT_FULL_NAME)
1664                         {
1665                             strcpyW(str, fullName);
1666                             str += strlenW(fullName);
1667                         }
1668                         else
1669                         {
1670                             strcpyW(str, rdnName);
1671                             str += strlenW(rdnName);
1672                         }
1673                         strcpyW(str, nameSep);
1674                         str += strlenW(nameSep);
1675                         ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 3,
1676                          &distPoint->DistPointName.u.FullName, str,
1677                          &altNameSize);
1678                         if (ret)
1679                             str += altNameSize / sizeof(WCHAR) - 1;
1680                     }
1681                     else if (distPoint->ReasonFlags.cbData)
1682                     {
1683                         DWORD reasonSize = bytesNeeded;
1684
1685                         strcpyW(str, reason);
1686                         str += strlenW(reason);
1687                         ret = CRYPT_FormatReason(dwFormatStrType,
1688                          &distPoint->ReasonFlags, str, &reasonSize);
1689                         if (ret)
1690                             str += reasonSize / sizeof(WCHAR) - 1;
1691                     }
1692                     else if (distPoint->CRLIssuer.cAltEntry)
1693                     {
1694                         DWORD crlIssuerSize = bytesNeeded;
1695
1696                         strcpyW(str, issuer);
1697                         str += strlenW(issuer);
1698                         strcpyW(str, nameSep);
1699                         str += strlenW(nameSep);
1700                         ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 2,
1701                          &distPoint->CRLIssuer, str,
1702                          &crlIssuerSize);
1703                         if (ret)
1704                             str += crlIssuerSize / sizeof(WCHAR) - 1;
1705                     }
1706                 }
1707             }
1708         }
1709         LocalFree(info);
1710     }
1711     return ret;
1712 }
1713
1714 static BOOL WINAPI CRYPT_FormatEnhancedKeyUsage(DWORD dwCertEncodingType,
1715  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
1716  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
1717  DWORD *pcbFormat)
1718 {
1719     CERT_ENHKEY_USAGE *usage;
1720     DWORD size;
1721     BOOL ret = FALSE;
1722
1723     if (!cbEncoded)
1724     {
1725         SetLastError(E_INVALIDARG);
1726         return FALSE;
1727     }
1728     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_ENHANCED_KEY_USAGE,
1729      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &usage, &size)))
1730     {
1731         WCHAR unknown[MAX_STRING_RESOURCE_LEN];
1732         DWORD i;
1733         DWORD bytesNeeded = sizeof(WCHAR); /* space for the NULL terminator */
1734         LPCWSTR sep;
1735         DWORD sepLen;
1736
1737         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1738         {
1739             sep = crlf;
1740             sepLen = strlenW(crlf) * sizeof(WCHAR);
1741         }
1742         else
1743         {
1744             sep = commaSpace;
1745             sepLen = strlenW(commaSpace) * sizeof(WCHAR);
1746         }
1747
1748         LoadStringW(hInstance, IDS_USAGE_UNKNOWN, unknown,
1749          sizeof(unknown) / sizeof(unknown[0]));
1750         for (i = 0; i < usage->cUsageIdentifier; i++)
1751         {
1752             PCCRYPT_OID_INFO info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
1753              usage->rgpszUsageIdentifier[i], CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
1754
1755             if (info)
1756                 bytesNeeded += strlenW(info->pwszName) * sizeof(WCHAR);
1757             else
1758                 bytesNeeded += strlenW(unknown) * sizeof(WCHAR);
1759             bytesNeeded += sizeof(WCHAR); /* space */
1760             bytesNeeded += sizeof(WCHAR); /* left paren */
1761             bytesNeeded += strlen(usage->rgpszUsageIdentifier[i]) *
1762              sizeof(WCHAR);
1763             bytesNeeded += sizeof(WCHAR); /* right paren */
1764             if (i < usage->cUsageIdentifier - 1)
1765                 bytesNeeded += sepLen;
1766         }
1767         if (!pbFormat)
1768             *pcbFormat = bytesNeeded;
1769         else if (*pcbFormat < bytesNeeded)
1770         {
1771             *pcbFormat = bytesNeeded;
1772             SetLastError(ERROR_MORE_DATA);
1773             ret = FALSE;
1774         }
1775         else
1776         {
1777             LPWSTR str = pbFormat;
1778
1779             *pcbFormat = bytesNeeded;
1780             for (i = 0; i < usage->cUsageIdentifier; i++)
1781             {
1782                 PCCRYPT_OID_INFO info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
1783                  usage->rgpszUsageIdentifier[i],
1784                  CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
1785                 LPCSTR oidPtr;
1786
1787                 if (info)
1788                 {
1789                     strcpyW(str, info->pwszName);
1790                     str += strlenW(info->pwszName);
1791                 }
1792                 else
1793                 {
1794                     strcpyW(str, unknown);
1795                     str += strlenW(unknown);
1796                 }
1797                 *str++ = ' ';
1798                 *str++ = '(';
1799                 for (oidPtr = usage->rgpszUsageIdentifier[i]; *oidPtr; oidPtr++)
1800                     *str++ = *oidPtr;
1801                 *str++ = ')';
1802                 *str = 0;
1803                 if (i < usage->cUsageIdentifier - 1)
1804                 {
1805                     strcpyW(str, sep);
1806                     str += sepLen / sizeof(WCHAR);
1807                 }
1808             }
1809         }
1810         LocalFree(usage);
1811     }
1812     return ret;
1813 }
1814
1815 static WCHAR financialCriteria[MAX_STRING_RESOURCE_LEN];
1816 static WCHAR available[MAX_STRING_RESOURCE_LEN];
1817 static WCHAR notAvailable[MAX_STRING_RESOURCE_LEN];
1818 static WCHAR meetsCriteria[MAX_STRING_RESOURCE_LEN];
1819 static WCHAR yes[MAX_STRING_RESOURCE_LEN];
1820 static WCHAR no[MAX_STRING_RESOURCE_LEN];
1821
1822 static BOOL WINAPI CRYPT_FormatSpcFinancialCriteria(DWORD dwCertEncodingType,
1823  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
1824  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
1825  DWORD *pcbFormat)
1826 {
1827     SPC_FINANCIAL_CRITERIA criteria;
1828     DWORD size = sizeof(criteria);
1829     BOOL ret = FALSE;
1830
1831     if (!cbEncoded)
1832     {
1833         SetLastError(E_INVALIDARG);
1834         return FALSE;
1835     }
1836     if ((ret = CryptDecodeObjectEx(dwCertEncodingType,
1837      SPC_FINANCIAL_CRITERIA_STRUCT, pbEncoded, cbEncoded, 0, NULL, &criteria,
1838      &size)))
1839     {
1840         static BOOL stringsLoaded = FALSE;
1841         DWORD bytesNeeded = sizeof(WCHAR);
1842         LPCWSTR sep;
1843         DWORD sepLen;
1844
1845         if (!stringsLoaded)
1846         {
1847             LoadStringW(hInstance, IDS_FINANCIAL_CRITERIA, financialCriteria,
1848              sizeof(financialCriteria) / sizeof(financialCriteria[0]));
1849             LoadStringW(hInstance, IDS_FINANCIAL_CRITERIA_AVAILABLE, available,
1850              sizeof(available) / sizeof(available[0]));
1851             LoadStringW(hInstance, IDS_FINANCIAL_CRITERIA_NOT_AVAILABLE,
1852              notAvailable, sizeof(notAvailable) / sizeof(notAvailable[0]));
1853             LoadStringW(hInstance, IDS_FINANCIAL_CRITERIA_MEETS_CRITERIA,
1854              meetsCriteria, sizeof(meetsCriteria) / sizeof(meetsCriteria[0]));
1855             LoadStringW(hInstance, IDS_YES, yes, sizeof(yes) / sizeof(yes[0]));
1856             LoadStringW(hInstance, IDS_NO, no, sizeof(no) / sizeof(no[0]));
1857             stringsLoaded = TRUE;
1858         }
1859         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1860         {
1861             sep = crlf;
1862             sepLen = strlenW(crlf) * sizeof(WCHAR);
1863         }
1864         else
1865         {
1866             sep = commaSpace;
1867             sepLen = strlenW(commaSpace) * sizeof(WCHAR);
1868         }
1869         bytesNeeded += strlenW(financialCriteria) * sizeof(WCHAR);
1870         if (criteria.fFinancialInfoAvailable)
1871         {
1872             bytesNeeded += strlenW(available) * sizeof(WCHAR);
1873             bytesNeeded += sepLen;
1874             bytesNeeded += strlenW(meetsCriteria) * sizeof(WCHAR);
1875             if (criteria.fMeetsCriteria)
1876                 bytesNeeded += strlenW(yes) * sizeof(WCHAR);
1877             else
1878                 bytesNeeded += strlenW(no) * sizeof(WCHAR);
1879         }
1880         else
1881             bytesNeeded += strlenW(notAvailable) * sizeof(WCHAR);
1882         if (!pbFormat)
1883             *pcbFormat = bytesNeeded;
1884         else if (*pcbFormat < bytesNeeded)
1885         {
1886             *pcbFormat = bytesNeeded;
1887             SetLastError(ERROR_MORE_DATA);
1888             ret = FALSE;
1889         }
1890         else
1891         {
1892             LPWSTR str = pbFormat;
1893
1894             *pcbFormat = bytesNeeded;
1895             strcpyW(str, financialCriteria);
1896             str += strlenW(financialCriteria);
1897             if (criteria.fFinancialInfoAvailable)
1898             {
1899                 strcpyW(str, available);
1900                 str += strlenW(available);
1901                 strcpyW(str, sep);
1902                 str += sepLen / sizeof(WCHAR);
1903                 strcpyW(str, meetsCriteria);
1904                 str += strlenW(meetsCriteria);
1905                 if (criteria.fMeetsCriteria)
1906                     strcpyW(str, yes);
1907                 else
1908                     strcpyW(str, no);
1909             }
1910             else
1911             {
1912                 strcpyW(str, notAvailable);
1913                 str += strlenW(notAvailable);
1914             }
1915         }
1916     }
1917     return ret;
1918 }
1919
1920 typedef BOOL (WINAPI *CryptFormatObjectFunc)(DWORD, DWORD, DWORD, void *,
1921  LPCSTR, const BYTE *, DWORD, void *, DWORD *);
1922
1923 static CryptFormatObjectFunc CRYPT_GetBuiltinFormatFunction(DWORD encodingType,
1924  DWORD formatStrType, LPCSTR lpszStructType)
1925 {
1926     CryptFormatObjectFunc format = NULL;
1927
1928     if ((encodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING)
1929     {
1930         SetLastError(ERROR_FILE_NOT_FOUND);
1931         return NULL;
1932     }
1933     if (!HIWORD(lpszStructType))
1934     {
1935         switch (LOWORD(lpszStructType))
1936         {
1937         case LOWORD(X509_BASIC_CONSTRAINTS2):
1938             format = CRYPT_FormatBasicConstraints2;
1939             break;
1940         case LOWORD(X509_AUTHORITY_KEY_ID2):
1941             format = CRYPT_FormatAuthorityKeyId2;
1942             break;
1943         case LOWORD(X509_AUTHORITY_INFO_ACCESS):
1944             format = CRYPT_FormatAuthorityInfoAccess;
1945             break;
1946         case LOWORD(X509_CRL_DIST_POINTS):
1947             format = CRYPT_FormatCRLDistPoints;
1948             break;
1949         case LOWORD(X509_ENHANCED_KEY_USAGE):
1950             format = CRYPT_FormatEnhancedKeyUsage;
1951             break;
1952         case LOWORD(SPC_FINANCIAL_CRITERIA_STRUCT):
1953             format = CRYPT_FormatSpcFinancialCriteria;
1954             break;
1955         }
1956     }
1957     else if (!strcmp(lpszStructType, szOID_BASIC_CONSTRAINTS2))
1958         format = CRYPT_FormatBasicConstraints2;
1959     else if (!strcmp(lpszStructType, szOID_AUTHORITY_INFO_ACCESS))
1960         format = CRYPT_FormatAuthorityInfoAccess;
1961     else if (!strcmp(lpszStructType, szOID_AUTHORITY_KEY_IDENTIFIER2))
1962         format = CRYPT_FormatAuthorityKeyId2;
1963     else if (!strcmp(lpszStructType, szOID_CRL_DIST_POINTS))
1964         format = CRYPT_FormatCRLDistPoints;
1965     else if (!strcmp(lpszStructType, szOID_ENHANCED_KEY_USAGE))
1966         format = CRYPT_FormatEnhancedKeyUsage;
1967     else if (!strcmp(lpszStructType, SPC_FINANCIAL_CRITERIA_OBJID))
1968         format = CRYPT_FormatSpcFinancialCriteria;
1969     if (!format && !(formatStrType & CRYPT_FORMAT_STR_NO_HEX))
1970         format = CRYPT_FormatHexString;
1971     return format;
1972 }
1973
1974 BOOL WINAPI CryptFormatObject(DWORD dwCertEncodingType, DWORD dwFormatType,
1975  DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType,
1976  const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat)
1977 {
1978     CryptFormatObjectFunc format = NULL;
1979     HCRYPTOIDFUNCADDR hFunc = NULL;
1980     BOOL ret = FALSE;
1981
1982     TRACE("(%08x, %d, %08x, %p, %s, %p, %d, %p, %p)\n", dwCertEncodingType,
1983      dwFormatType, dwFormatStrType, pFormatStruct, debugstr_a(lpszStructType),
1984      pbEncoded, cbEncoded, pbFormat, pcbFormat);
1985
1986     if (!(format = CRYPT_GetBuiltinFormatFunction(dwCertEncodingType,
1987      dwFormatStrType, lpszStructType)))
1988     {
1989         static HCRYPTOIDFUNCSET set = NULL;
1990
1991         if (!set)
1992             set = CryptInitOIDFunctionSet(CRYPT_OID_FORMAT_OBJECT_FUNC, 0);
1993         CryptGetOIDFunctionAddress(set, dwCertEncodingType, lpszStructType, 0,
1994          (void **)&format, &hFunc);
1995     }
1996     if (format)
1997         ret = format(dwCertEncodingType, dwFormatType, dwFormatStrType,
1998          pFormatStruct, lpszStructType, pbEncoded, cbEncoded, pbFormat,
1999          pcbFormat);
2000     if (hFunc)
2001         CryptFreeOIDFunctionAddress(hFunc, 0);
2002     return ret;
2003 }