crypt32: Check registered CryptFormatObject functions before using default hex format.
[wine] / dlls / crypt32 / str.c
1 /*
2  * Copyright 2006 Juan Lang for CodeWeavers
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 "winnls.h"
22 #include "winuser.h"
23 #include "wincrypt.h"
24 #include "wine/debug.h"
25 #include "wine/unicode.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
28
29 DWORD WINAPI CertRDNValueToStrA(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
30  LPSTR psz, DWORD csz)
31 {
32     DWORD ret = 0;
33
34     TRACE("(%d, %p, %p, %d)\n", dwValueType, pValue, psz, csz);
35
36     switch (dwValueType)
37     {
38     case CERT_RDN_ANY_TYPE:
39         break;
40     case CERT_RDN_NUMERIC_STRING:
41     case CERT_RDN_PRINTABLE_STRING:
42     case CERT_RDN_TELETEX_STRING:
43     case CERT_RDN_VIDEOTEX_STRING:
44     case CERT_RDN_IA5_STRING:
45     case CERT_RDN_GRAPHIC_STRING:
46     case CERT_RDN_VISIBLE_STRING:
47     case CERT_RDN_GENERAL_STRING:
48         if (!psz || !csz)
49             ret = pValue->cbData;
50         else
51         {
52             DWORD chars = min(pValue->cbData, csz - 1);
53
54             if (chars)
55             {
56                 memcpy(psz, pValue->pbData, chars);
57                 ret += chars;
58                 csz -= chars;
59             }
60         }
61         break;
62     case CERT_RDN_UTF8_STRING:
63         if (!psz || !csz)
64             ret = WideCharToMultiByte(CP_UTF8, 0, (LPWSTR)pValue->pbData,
65              pValue->cbData / sizeof(WCHAR) + 1, NULL, 0, NULL, NULL);
66         else
67         {
68             ret = WideCharToMultiByte(CP_UTF8, 0, (LPWSTR)pValue->pbData,
69              pValue->cbData / sizeof(WCHAR) + 1, psz, csz - 1, NULL, NULL);
70             csz -= ret;
71         }
72         break;
73     default:
74         FIXME("string type %d unimplemented\n", dwValueType);
75     }
76     if (psz && csz)
77     {
78         *(psz + ret) = '\0';
79         csz--;
80         ret++;
81     }
82     else
83         ret++;
84     TRACE("returning %d (%s)\n", ret, debugstr_a(psz));
85     return ret;
86 }
87
88 DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
89  LPWSTR psz, DWORD csz)
90 {
91     DWORD ret = 0;
92
93     TRACE("(%d, %p, %p, %d)\n", dwValueType, pValue, psz, csz);
94
95     switch (dwValueType)
96     {
97     case CERT_RDN_ANY_TYPE:
98         break;
99     case CERT_RDN_NUMERIC_STRING:
100     case CERT_RDN_PRINTABLE_STRING:
101     case CERT_RDN_TELETEX_STRING:
102     case CERT_RDN_VIDEOTEX_STRING:
103     case CERT_RDN_IA5_STRING:
104     case CERT_RDN_GRAPHIC_STRING:
105     case CERT_RDN_VISIBLE_STRING:
106     case CERT_RDN_GENERAL_STRING:
107         if (!psz || !csz)
108             ret = pValue->cbData;
109         else
110         {
111             DWORD chars = min(pValue->cbData, csz - 1);
112
113             if (chars)
114             {
115                 DWORD i;
116
117                 for (i = 0; i < chars; i++)
118                     psz[i] = pValue->pbData[i];
119                 ret += chars;
120                 csz -= chars;
121             }
122         }
123         break;
124     case CERT_RDN_UTF8_STRING:
125         if (!psz || !csz)
126             ret = pValue->cbData / sizeof(WCHAR);
127         else
128         {
129             DWORD chars = min(pValue->cbData / sizeof(WCHAR), csz - 1);
130
131             if (chars)
132             {
133                 DWORD i;
134
135                 for (i = 0; i < chars; i++)
136                     psz[i] = *((LPWSTR)pValue->pbData + i);
137                 ret += chars;
138                 csz -= chars;
139             }
140         }
141         break;
142     default:
143         FIXME("string type %d unimplemented\n", dwValueType);
144     }
145     if (psz && csz)
146     {
147         *(psz + ret) = '\0';
148         csz--;
149         ret++;
150     }
151     else
152         ret++;
153     TRACE("returning %d (%s)\n", ret, debugstr_w(psz));
154     return ret;
155 }
156
157 /* Adds the prefix prefix to the string pointed to by psz, followed by the
158  * character '='.  Copies no more than csz characters.  Returns the number of
159  * characters copied.  If psz is NULL, returns the number of characters that
160  * would be copied.
161  */
162 static DWORD CRYPT_AddPrefixA(LPCSTR prefix, LPSTR psz, DWORD csz)
163 {
164     DWORD chars;
165
166     TRACE("(%s, %p, %d)\n", debugstr_a(prefix), psz, csz);
167
168     if (psz)
169     {
170         chars = min(strlen(prefix), csz);
171         memcpy(psz, prefix, chars);
172         *(psz + chars) = '=';
173         chars++;
174     }
175     else
176         chars = lstrlenA(prefix) + 1;
177     return chars;
178 }
179
180 DWORD WINAPI CertNameToStrA(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
181  DWORD dwStrType, LPSTR psz, DWORD csz)
182 {
183     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
184      CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
185     static const char commaSep[] = ", ";
186     static const char semiSep[] = "; ";
187     static const char crlfSep[] = "\r\n";
188     static const char plusSep[] = " + ";
189     static const char spaceSep[] = " ";
190     DWORD ret = 0, bytes = 0;
191     BOOL bRet;
192     CERT_NAME_INFO *info;
193
194     TRACE("(%d, %p, %08x, %p, %d)\n", dwCertEncodingType, pName, dwStrType,
195      psz, csz);
196     if (dwStrType & unsupportedFlags)
197         FIXME("unsupported flags: %08x\n", dwStrType & unsupportedFlags);
198
199     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
200      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
201     if (bRet)
202     {
203         DWORD i, j, sepLen, rdnSepLen;
204         LPCSTR sep, rdnSep;
205         BOOL reverse = dwStrType & CERT_NAME_STR_REVERSE_FLAG;
206         const CERT_RDN *rdn = info->rgRDN;
207
208         if(reverse && info->cRDN > 1) rdn += (info->cRDN - 1);
209
210         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
211             sep = semiSep;
212         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
213             sep = crlfSep;
214         else
215             sep = commaSep;
216         sepLen = strlen(sep);
217         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
218             rdnSep = spaceSep;
219         else
220             rdnSep = plusSep;
221         rdnSepLen = strlen(rdnSep);
222         for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++)
223         {
224             for (j = 0; (!psz || ret < csz) && j < rdn->cRDNAttr; j++)
225             {
226                 DWORD chars;
227                 char prefixBuf[10]; /* big enough for GivenName */
228                 LPCSTR prefix = NULL;
229
230                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
231                     prefix = rdn->rgRDNAttr[j].pszObjId;
232                 else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR)
233                 {
234                     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(
235                      CRYPT_OID_INFO_OID_KEY,
236                      rdn->rgRDNAttr[j].pszObjId,
237                      CRYPT_RDN_ATTR_OID_GROUP_ID);
238
239                     if (oidInfo)
240                     {
241                         WideCharToMultiByte(CP_ACP, 0, oidInfo->pwszName, -1,
242                          prefixBuf, sizeof(prefixBuf), NULL, NULL);
243                         prefix = prefixBuf;
244                     }
245                     else
246                         prefix = rdn->rgRDNAttr[j].pszObjId;
247                 }
248                 if (prefix)
249                 {
250                     /* - 1 is needed to account for the NULL terminator. */
251                     chars = CRYPT_AddPrefixA(prefix,
252                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
253                     ret += chars;
254                 }
255                 /* FIXME: handle quoting */
256                 chars = CertRDNValueToStrA(
257                  rdn->rgRDNAttr[j].dwValueType,
258                  &rdn->rgRDNAttr[j].Value, psz ? psz + ret : NULL,
259                  psz ? csz - ret : 0);
260                 if (chars)
261                     ret += chars - 1;
262                 if (j < rdn->cRDNAttr - 1)
263                 {
264                     if (psz && ret < csz - rdnSepLen - 1)
265                         memcpy(psz + ret, rdnSep, rdnSepLen);
266                     ret += rdnSepLen;
267                 }
268             }
269             if (i < info->cRDN - 1)
270             {
271                 if (psz && ret < csz - sepLen - 1)
272                     memcpy(psz + ret, sep, sepLen);
273                 ret += sepLen;
274             }
275             if(reverse) rdn--;
276             else rdn++;
277         }
278         LocalFree(info);
279     }
280     if (psz && csz)
281     {
282         *(psz + ret) = '\0';
283         ret++;
284     }
285     else
286         ret++;
287     TRACE("Returning %s\n", debugstr_a(psz));
288     return ret;
289 }
290
291 /* Adds the prefix prefix to the wide-character string pointed to by psz,
292  * followed by the character '='.  Copies no more than csz characters.  Returns
293  * the number of characters copied.  If psz is NULL, returns the number of
294  * characters that would be copied.
295  * Assumes the characters in prefix are ASCII (not multibyte characters.)
296  */
297 static DWORD CRYPT_AddPrefixAToW(LPCSTR prefix, LPWSTR psz, DWORD csz)
298 {
299     DWORD chars;
300
301     TRACE("(%s, %p, %d)\n", debugstr_a(prefix), psz, csz);
302
303     if (psz)
304     {
305         DWORD i;
306
307         chars = min(strlen(prefix), csz);
308         for (i = 0; i < chars; i++)
309             *(psz + i) = prefix[i];
310         *(psz + chars) = '=';
311         chars++;
312     }
313     else
314         chars = lstrlenA(prefix) + 1;
315     return chars;
316 }
317
318 /* Adds the prefix prefix to the string pointed to by psz, followed by the
319  * character '='.  Copies no more than csz characters.  Returns the number of
320  * characters copied.  If psz is NULL, returns the number of characters that
321  * would be copied.
322  */
323 static DWORD CRYPT_AddPrefixW(LPCWSTR prefix, LPWSTR psz, DWORD csz)
324 {
325     DWORD chars;
326
327     TRACE("(%s, %p, %d)\n", debugstr_w(prefix), psz, csz);
328
329     if (psz)
330     {
331         chars = min(strlenW(prefix), csz);
332         memcpy(psz, prefix, chars * sizeof(WCHAR));
333         *(psz + chars) = '=';
334         chars++;
335     }
336     else
337         chars = lstrlenW(prefix) + 1;
338     return chars;
339 }
340
341 static const WCHAR indent[] = { ' ',' ',' ',' ',' ',0 };
342
343 DWORD cert_name_to_str_with_indent(DWORD dwCertEncodingType, DWORD indentLevel,
344  PCERT_NAME_BLOB pName, DWORD dwStrType, LPWSTR psz, DWORD csz)
345 {
346     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
347      CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
348     static const WCHAR commaSep[] = { ',',' ',0 };
349     static const WCHAR semiSep[] = { ';',' ',0 };
350     static const WCHAR crlfSep[] = { '\r','\n',0 };
351     static const WCHAR plusSep[] = { ' ','+',' ',0 };
352     static const WCHAR spaceSep[] = { ' ',0 };
353     DWORD ret = 0, bytes = 0;
354     BOOL bRet;
355     CERT_NAME_INFO *info;
356
357     if (dwStrType & unsupportedFlags)
358         FIXME("unsupported flags: %08x\n", dwStrType & unsupportedFlags);
359
360     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
361      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
362     if (bRet)
363     {
364         DWORD i, j, sepLen, rdnSepLen;
365         LPCWSTR sep, rdnSep;
366         BOOL reverse = dwStrType & CERT_NAME_STR_REVERSE_FLAG;
367         const CERT_RDN *rdn = info->rgRDN;
368
369         if(reverse && info->cRDN > 1) rdn += (info->cRDN - 1);
370
371         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
372             sep = semiSep;
373         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
374             sep = crlfSep;
375         else
376             sep = commaSep;
377         sepLen = lstrlenW(sep);
378         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
379             rdnSep = spaceSep;
380         else
381             rdnSep = plusSep;
382         rdnSepLen = lstrlenW(rdnSep);
383         for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++)
384         {
385             for (j = 0; (!psz || ret < csz) && j < rdn->cRDNAttr; j++)
386             {
387                 DWORD chars;
388                 LPCSTR prefixA = NULL;
389                 LPCWSTR prefixW = NULL;
390
391                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
392                     prefixA = rdn->rgRDNAttr[j].pszObjId;
393                 else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR)
394                 {
395                     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(
396                      CRYPT_OID_INFO_OID_KEY,
397                      rdn->rgRDNAttr[j].pszObjId,
398                      CRYPT_RDN_ATTR_OID_GROUP_ID);
399
400                     if (oidInfo)
401                         prefixW = oidInfo->pwszName;
402                     else
403                         prefixA = rdn->rgRDNAttr[j].pszObjId;
404                 }
405                 if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
406                 {
407                     DWORD k;
408
409                     for (k = 0; k < indentLevel; k++)
410                     {
411                         if (psz)
412                         {
413                             chars = min(strlenW(indent), csz - ret - 1);
414                             memcpy(psz + ret, indent, chars * sizeof(WCHAR));
415                         }
416                         else
417                             chars = strlenW(indent);
418                         ret += chars;
419                     }
420                 }
421                 if (prefixW)
422                 {
423                     /* - 1 is needed to account for the NULL terminator. */
424                     chars = CRYPT_AddPrefixW(prefixW,
425                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
426                     ret += chars;
427                 }
428                 else if (prefixA)
429                 {
430                     /* - 1 is needed to account for the NULL terminator. */
431                     chars = CRYPT_AddPrefixAToW(prefixA,
432                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
433                     ret += chars;
434                 }
435                 /* FIXME: handle quoting */
436                 chars = CertRDNValueToStrW(
437                  rdn->rgRDNAttr[j].dwValueType,
438                  &rdn->rgRDNAttr[j].Value, psz ? psz + ret : NULL,
439                  psz ? csz - ret : 0);
440                 if (chars)
441                     ret += chars - 1;
442                 if (j < rdn->cRDNAttr - 1)
443                 {
444                     if (psz && ret < csz - rdnSepLen - 1)
445                         memcpy(psz + ret, rdnSep, rdnSepLen * sizeof(WCHAR));
446                     ret += rdnSepLen;
447                 }
448             }
449             if (i < info->cRDN - 1)
450             {
451                 if (psz && ret < csz - sepLen - 1)
452                     memcpy(psz + ret, sep, sepLen * sizeof(WCHAR));
453                 ret += sepLen;
454             }
455             if(reverse) rdn--;
456             else rdn++;
457         }
458         LocalFree(info);
459     }
460     if (psz && csz)
461     {
462         *(psz + ret) = '\0';
463         ret++;
464     }
465     else
466         ret++;
467     return ret;
468 }
469
470 DWORD WINAPI CertNameToStrW(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
471  DWORD dwStrType, LPWSTR psz, DWORD csz)
472 {
473     BOOL ret;
474
475     TRACE("(%d, %p, %08x, %p, %d)\n", dwCertEncodingType, pName, dwStrType,
476      psz, csz);
477
478     ret = cert_name_to_str_with_indent(dwCertEncodingType, 0, pName, dwStrType,
479      psz, csz);
480     TRACE("Returning %s\n", debugstr_w(psz));
481     return ret;
482 }
483
484 BOOL WINAPI CertStrToNameA(DWORD dwCertEncodingType, LPCSTR pszX500,
485  DWORD dwStrType, void *pvReserved, BYTE *pbEncoded, DWORD *pcbEncoded,
486  LPCSTR *ppszError)
487 {
488     BOOL ret;
489     int len;
490
491     TRACE("(%08x, %s, %08x, %p, %p, %p, %p)\n", dwCertEncodingType,
492      debugstr_a(pszX500), dwStrType, pvReserved, pbEncoded, pcbEncoded,
493      ppszError);
494
495     len = MultiByteToWideChar(CP_ACP, 0, pszX500, -1, NULL, 0);
496     if (len)
497     {
498         LPWSTR x500, errorStr;
499
500         if ((x500 = CryptMemAlloc(len * sizeof(WCHAR))))
501         {
502             MultiByteToWideChar(CP_ACP, 0, pszX500, -1, x500, len);
503             ret = CertStrToNameW(dwCertEncodingType, x500, dwStrType,
504              pvReserved, pbEncoded, pcbEncoded,
505              ppszError ? (LPCWSTR *)&errorStr : NULL);
506             if (ppszError)
507             {
508                 if (!ret)
509                 {
510                     LONG i;
511
512                     *ppszError = pszX500;
513                     for (i = 0; i < errorStr - x500; i++)
514                         *ppszError = CharNextA(*ppszError);
515                 }
516                 else
517                     *ppszError = NULL;
518             }
519             CryptMemFree(x500);
520         }
521         else
522         {
523             SetLastError(ERROR_OUTOFMEMORY);
524             ret = FALSE;
525         }
526     }
527     else
528     {
529         SetLastError(CRYPT_E_INVALID_X500_STRING);
530         if (ppszError)
531             *ppszError = pszX500;
532         ret = FALSE;
533     }
534     return ret;
535 }
536
537 struct KeynameKeeper
538 {
539     WCHAR  buf[10]; /* big enough for L"GivenName" */
540     LPWSTR keyName; /* usually = buf, but may be allocated */
541     DWORD  keyLen;
542 };
543
544 static void CRYPT_InitializeKeynameKeeper(struct KeynameKeeper *keeper)
545 {
546     keeper->keyName = keeper->buf;
547     keeper->keyLen = sizeof(keeper->buf) / sizeof(keeper->buf[0]);
548 }
549
550 static void CRYPT_FreeKeynameKeeper(struct KeynameKeeper *keeper)
551 {
552     if (keeper->keyName != keeper->buf)
553         CryptMemFree(keeper->keyName);
554 }
555
556 struct X500TokenW
557 {
558     LPCWSTR start;
559     LPCWSTR end;
560 };
561
562 static void CRYPT_KeynameKeeperFromTokenW(struct KeynameKeeper *keeper,
563  const struct X500TokenW *key)
564 {
565     DWORD len = key->end - key->start;
566
567     if (len > keeper->keyLen)
568     {
569         if (keeper->keyName == keeper->buf)
570             keeper->keyName = CryptMemAlloc(len * sizeof(WCHAR));
571         else
572             keeper->keyName = CryptMemRealloc(keeper->keyName,
573              len * sizeof(WCHAR));
574         keeper->keyLen = len;
575     }
576     memcpy(keeper->keyName, key->start, (key->end - key->start) *
577      sizeof(WCHAR));
578     keeper->keyName[len] = '\0';
579     TRACE("Keyname is %s\n", debugstr_w(keeper->keyName));
580 }
581
582 static BOOL CRYPT_GetNextKeyW(LPCWSTR str, struct X500TokenW *token,
583  LPCWSTR *ppszError)
584 {
585     BOOL ret = TRUE;
586
587     while (*str && isspaceW(*str))
588         str++;
589     if (*str)
590     {
591         token->start = str;
592         while (*str && *str != '=' && !isspaceW(*str))
593             str++;
594         if (*str && (*str == '=' || isspaceW(*str)))
595             token->end = str;
596         else
597         {
598             TRACE("missing equals char at %s\n", debugstr_w(token->start));
599             if (ppszError)
600                 *ppszError = token->start;
601             SetLastError(CRYPT_E_INVALID_X500_STRING);
602             ret = FALSE;
603         }
604     }
605     else
606         token->start = NULL;
607     return ret;
608 }
609
610 /* Assumes separators are characters in the 0-255 range */
611 static BOOL CRYPT_GetNextValueW(LPCWSTR str, DWORD dwFlags, LPCWSTR separators,
612  struct X500TokenW *token, LPCWSTR *ppszError)
613 {
614     BOOL ret = TRUE;
615
616     TRACE("(%s, %s, %p, %p)\n", debugstr_w(str), debugstr_w(separators), token,
617      ppszError);
618
619     while (*str && isspaceW(*str))
620         str++;
621     if (*str)
622     {
623         token->start = str;
624         if (!(dwFlags & CERT_NAME_STR_NO_QUOTING_FLAG) && *str == '"')
625         {
626             token->end = NULL;
627             str++;
628             while (!token->end && ret)
629             {
630                 while (*str && *str != '"')
631                     str++;
632                 if (*str == '"')
633                 {
634                     if (*(str + 1) != '"')
635                         token->end = str + 1;
636                     else
637                         str += 2;
638                 }
639                 else
640                 {
641                     TRACE("unterminated quote at %s\n", debugstr_w(str));
642                     if (ppszError)
643                         *ppszError = str;
644                     SetLastError(CRYPT_E_INVALID_X500_STRING);
645                     ret = FALSE;
646                 }
647             }
648         }
649         else
650         {
651             WCHAR map[256] = { 0 };
652
653             while (*separators)
654                 map[*separators++] = 1;
655             while (*str && (*str >= 0xff || !map[*str]))
656                 str++;
657             token->end = str;
658         }
659     }
660     else
661     {
662         TRACE("missing value at %s\n", debugstr_w(str));
663         if (ppszError)
664             *ppszError = str;
665         SetLastError(CRYPT_E_INVALID_X500_STRING);
666         ret = FALSE;
667     }
668     return ret;
669 }
670
671 /* Encodes the string represented by value as the string type type into the
672  * CERT_NAME_BLOB output.  If there is an error and ppszError is not NULL,
673  * *ppszError is set to the first failing character.  If there is no error,
674  * output's pbData must be freed with LocalFree.
675  */
676 static BOOL CRYPT_EncodeValueWithType(DWORD dwCertEncodingType,
677  const struct X500TokenW *value, PCERT_NAME_BLOB output, DWORD type,
678  LPCWSTR *ppszError)
679 {
680     CERT_NAME_VALUE nameValue = { type, { 0, NULL } };
681     BOOL ret = TRUE;
682
683     if (value->end > value->start)
684     {
685         nameValue.Value.pbData = CryptMemAlloc((value->end - value->start) *
686          sizeof(WCHAR));
687         if (!nameValue.Value.pbData)
688         {
689             SetLastError(ERROR_OUTOFMEMORY);
690             ret = FALSE;
691         }
692     }
693     if (ret)
694     {
695         if (value->end > value->start)
696         {
697             LONG i;
698             LPWSTR ptr = (LPWSTR)nameValue.Value.pbData;
699
700             for (i = 0; i < value->end - value->start; i++)
701             {
702                 *ptr++ = value->start[i];
703                 if (value->start[i] == '"')
704                     i++;
705             }
706             nameValue.Value.cbData = (LPBYTE)ptr - nameValue.Value.pbData;
707         }
708         ret = CryptEncodeObjectEx(dwCertEncodingType, X509_UNICODE_NAME_VALUE,
709          &nameValue, CRYPT_ENCODE_ALLOC_FLAG, NULL, &output->pbData,
710          &output->cbData);
711         if (!ret && ppszError)
712         {
713             if (type == CERT_RDN_NUMERIC_STRING &&
714              GetLastError() == CRYPT_E_INVALID_NUMERIC_STRING)
715                 *ppszError = value->start + output->cbData;
716             else if (type == CERT_RDN_PRINTABLE_STRING &&
717              GetLastError() == CRYPT_E_INVALID_PRINTABLE_STRING)
718                 *ppszError = value->start + output->cbData;
719             else if (type == CERT_RDN_IA5_STRING &&
720              GetLastError() == CRYPT_E_INVALID_IA5_STRING)
721                 *ppszError = value->start + output->cbData;
722         }
723         CryptMemFree(nameValue.Value.pbData);
724     }
725     return ret;
726 }
727
728 static BOOL CRYPT_EncodeValue(DWORD dwCertEncodingType,
729  const struct X500TokenW *value, PCERT_NAME_BLOB output, const DWORD *types,
730  LPCWSTR *ppszError)
731 {
732     DWORD i;
733     BOOL ret;
734
735     ret = FALSE;
736     for (i = 0; !ret && types[i]; i++)
737         ret = CRYPT_EncodeValueWithType(dwCertEncodingType, value, output,
738          types[i], ppszError);
739     return ret;
740 }
741
742 static BOOL CRYPT_ValueToRDN(DWORD dwCertEncodingType, PCERT_NAME_INFO info,
743  PCCRYPT_OID_INFO keyOID, struct X500TokenW *value, LPCWSTR *ppszError)
744 {
745     BOOL ret = FALSE;
746
747     TRACE("OID %s, value %s\n", debugstr_a(keyOID->pszOID),
748      debugstr_wn(value->start, value->end - value->start));
749
750     if (!info->rgRDN)
751         info->rgRDN = CryptMemAlloc(sizeof(CERT_RDN));
752     else
753         info->rgRDN = CryptMemRealloc(info->rgRDN,
754          (info->cRDN + 1) * sizeof(CERT_RDN));
755     if (info->rgRDN)
756     {
757         /* FIXME: support multiple RDN attrs */
758         info->rgRDN[info->cRDN].rgRDNAttr =
759          CryptMemAlloc(sizeof(CERT_RDN_ATTR));
760         if (info->rgRDN[info->cRDN].rgRDNAttr)
761         {
762             static const DWORD defaultTypes[] = { CERT_RDN_PRINTABLE_STRING,
763              CERT_RDN_BMP_STRING, 0 };
764             const DWORD *types;
765
766             info->rgRDN[info->cRDN].cRDNAttr = 1;
767             info->rgRDN[info->cRDN].rgRDNAttr[0].pszObjId =
768              (LPSTR)keyOID->pszOID;
769             info->rgRDN[info->cRDN].rgRDNAttr[0].dwValueType =
770              CERT_RDN_ENCODED_BLOB;
771             if (keyOID->ExtraInfo.cbData)
772                 types = (const DWORD *)keyOID->ExtraInfo.pbData;
773             else
774                 types = defaultTypes;
775
776             /* Remove surrounding quotes */
777             if (value->start[0] == '"')
778             {
779                 value->start++;
780                 value->end--;
781             }
782             ret = CRYPT_EncodeValue(dwCertEncodingType, value,
783              &info->rgRDN[info->cRDN].rgRDNAttr[0].Value, types, ppszError);
784         }
785         else
786             SetLastError(ERROR_OUTOFMEMORY);
787         info->cRDN++;
788     }
789     else
790         SetLastError(ERROR_OUTOFMEMORY);
791     return ret;
792 }
793
794 BOOL WINAPI CertStrToNameW(DWORD dwCertEncodingType, LPCWSTR pszX500,
795  DWORD dwStrType, void *pvReserved, BYTE *pbEncoded, DWORD *pcbEncoded,
796  LPCWSTR *ppszError)
797 {
798     CERT_NAME_INFO info = { 0, NULL };
799     LPCWSTR str;
800     struct KeynameKeeper keeper;
801     DWORD i;
802     BOOL ret = TRUE;
803
804     TRACE("(%08x, %s, %08x, %p, %p, %p, %p)\n", dwCertEncodingType,
805      debugstr_w(pszX500), dwStrType, pvReserved, pbEncoded, pcbEncoded,
806      ppszError);
807
808     CRYPT_InitializeKeynameKeeper(&keeper);
809     str = pszX500;
810     while (str && *str && ret)
811     {
812         struct X500TokenW token;
813
814         ret = CRYPT_GetNextKeyW(str, &token, ppszError);
815         if (ret && token.start)
816         {
817             PCCRYPT_OID_INFO keyOID;
818
819             CRYPT_KeynameKeeperFromTokenW(&keeper, &token);
820             keyOID = CryptFindOIDInfo(CRYPT_OID_INFO_NAME_KEY, keeper.keyName,
821              CRYPT_RDN_ATTR_OID_GROUP_ID);
822             if (!keyOID)
823             {
824                 if (ppszError)
825                     *ppszError = token.start;
826                 SetLastError(CRYPT_E_INVALID_X500_STRING);
827                 ret = FALSE;
828             }
829             else
830             {
831                 str = token.end;
832                 while (isspace(*str))
833                     str++;
834                 if (*str != '=')
835                 {
836                     if (ppszError)
837                         *ppszError = str;
838                     SetLastError(CRYPT_E_INVALID_X500_STRING);
839                     ret = FALSE;
840                 }
841                 else
842                 {
843                     static const WCHAR commaSep[] = { ',',0 };
844                     static const WCHAR semiSep[] = { ';',0 };
845                     static const WCHAR crlfSep[] = { '\r','\n',0 };
846                     static const WCHAR allSeps[] = { ',',';','\r','\n',0 };
847                     LPCWSTR sep;
848
849                     str++;
850                     if (dwStrType & CERT_NAME_STR_COMMA_FLAG)
851                         sep = commaSep;
852                     else if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
853                         sep = semiSep;
854                     else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
855                         sep = crlfSep;
856                     else
857                         sep = allSeps;
858                     ret = CRYPT_GetNextValueW(str, dwStrType, sep, &token,
859                      ppszError);
860                     if (ret)
861                     {
862                         str = token.end;
863                         ret = CRYPT_ValueToRDN(dwCertEncodingType, &info,
864                          keyOID, &token, ppszError);
865                     }
866                 }
867             }
868         }
869     }
870     CRYPT_FreeKeynameKeeper(&keeper);
871     if (ret)
872     {
873         if (ppszError)
874             *ppszError = NULL;
875         ret = CryptEncodeObjectEx(dwCertEncodingType, X509_NAME, &info,
876          0, NULL, pbEncoded, pcbEncoded);
877     }
878     for (i = 0; i < info.cRDN; i++)
879     {
880         DWORD j;
881
882         for (j = 0; j < info.rgRDN[i].cRDNAttr; j++)
883             LocalFree(info.rgRDN[i].rgRDNAttr[j].Value.pbData);
884         CryptMemFree(info.rgRDN[i].rgRDNAttr);
885     }
886     CryptMemFree(info.rgRDN);
887     return ret;
888 }
889
890 DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType,
891  DWORD dwFlags, void *pvTypePara, LPSTR pszNameString, DWORD cchNameString)
892 {
893     DWORD ret;
894
895     TRACE("(%p, %d, %08x, %p, %p, %d)\n", pCertContext, dwType, dwFlags,
896      pvTypePara, pszNameString, cchNameString);
897
898     if (pszNameString)
899     {
900         LPWSTR wideName;
901         DWORD nameLen;
902
903         nameLen = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
904          NULL, 0);
905         wideName = CryptMemAlloc(nameLen * sizeof(WCHAR));
906         if (wideName)
907         {
908             CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
909              wideName, nameLen);
910             nameLen = WideCharToMultiByte(CP_ACP, 0, wideName, nameLen,
911              pszNameString, cchNameString, NULL, NULL);
912             if (nameLen <= cchNameString)
913                 ret = nameLen;
914             else
915             {
916                 pszNameString[cchNameString - 1] = '\0';
917                 ret = cchNameString;
918             }
919             CryptMemFree(wideName);
920         }
921         else
922         {
923             *pszNameString = '\0';
924             ret = 1;
925         }
926     }
927     else
928         ret = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
929          NULL, 0);
930     return ret;
931 }
932
933 DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
934  DWORD dwFlags, void *pvTypePara, LPWSTR pszNameString, DWORD cchNameString)
935 {
936     DWORD ret;
937     PCERT_NAME_BLOB name;
938     LPCSTR altNameOID;
939
940     TRACE("(%p, %d, %08x, %p, %p, %d)\n", pCertContext, dwType,
941      dwFlags, pvTypePara, pszNameString, cchNameString);
942
943     if (dwFlags & CERT_NAME_ISSUER_FLAG)
944     {
945         name = &pCertContext->pCertInfo->Issuer;
946         altNameOID = szOID_ISSUER_ALT_NAME;
947     }
948     else
949     {
950         name = &pCertContext->pCertInfo->Subject;
951         altNameOID = szOID_SUBJECT_ALT_NAME;
952     }
953
954     switch (dwType)
955     {
956     case CERT_NAME_SIMPLE_DISPLAY_TYPE:
957     {
958         static const LPCSTR simpleAttributeOIDs[] = { szOID_COMMON_NAME,
959          szOID_ORGANIZATIONAL_UNIT_NAME, szOID_ORGANIZATION_NAME,
960          szOID_RSA_emailAddr };
961         CERT_NAME_INFO *info = NULL;
962         PCERT_RDN_ATTR nameAttr = NULL;
963         DWORD bytes = 0, i;
964
965         if (CryptDecodeObjectEx(pCertContext->dwCertEncodingType, X509_NAME,
966          name->pbData, name->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info,
967          &bytes))
968         {
969             for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) /
970              sizeof(simpleAttributeOIDs[0]); i++)
971                 nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], info);
972         }
973         if (!nameAttr)
974         {
975             PCERT_EXTENSION ext = CertFindExtension(altNameOID,
976              pCertContext->pCertInfo->cExtension,
977              pCertContext->pCertInfo->rgExtension);
978
979             if (ext)
980             {
981                 for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) /
982                  sizeof(simpleAttributeOIDs[0]); i++)
983                     nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], info);
984                 if (!nameAttr)
985                 {
986                     /* FIXME: gotta then look for a rfc822Name choice in ext.
987                      * Failing that, look for the first attribute.
988                      */
989                     FIXME("CERT_NAME_SIMPLE_DISPLAY_TYPE: stub\n");
990                 }
991             }
992         }
993         if (nameAttr)
994             ret = CertRDNValueToStrW(nameAttr->dwValueType, &nameAttr->Value,
995                                      pszNameString, cchNameString);
996         else
997             ret = 0;
998         if (info)
999             LocalFree(info);
1000         break;
1001     }
1002     case CERT_NAME_FRIENDLY_DISPLAY_TYPE:
1003     {
1004         DWORD cch = cchNameString;
1005
1006         if (CertGetCertificateContextProperty(pCertContext,
1007          CERT_FRIENDLY_NAME_PROP_ID, pszNameString, &cch))
1008             ret = cch;
1009         else
1010             ret = CertGetNameStringW(pCertContext,
1011              CERT_NAME_SIMPLE_DISPLAY_TYPE, dwFlags, pvTypePara, pszNameString,
1012              cchNameString);
1013         break;
1014     }
1015     default:
1016         FIXME("unimplemented for type %d\n", dwType);
1017         ret = 0;
1018     }
1019     return ret;
1020 }