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