crypt32: Use a structure to hold function address handle.
[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(lstrlenA(prefix), csz);
171         memcpy(psz, prefix, chars);
172         csz -= chars;
173         *(psz + chars) = '=';
174         chars++;
175         csz--;
176     }
177     else
178         chars = lstrlenA(prefix) + 1;
179     return chars;
180 }
181
182 DWORD WINAPI CertNameToStrA(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
183  DWORD dwStrType, LPSTR psz, DWORD csz)
184 {
185     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
186      CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
187     static const char commaSep[] = ", ";
188     static const char semiSep[] = "; ";
189     static const char crlfSep[] = "\r\n";
190     static const char plusSep[] = " + ";
191     static const char spaceSep[] = " ";
192     DWORD ret = 0, bytes = 0;
193     BOOL bRet;
194     CERT_NAME_INFO *info;
195
196     TRACE("(%d, %p, %08x, %p, %d)\n", dwCertEncodingType, pName, dwStrType,
197      psz, csz);
198     if (dwStrType & unsupportedFlags)
199         FIXME("unsupported flags: %08x\n", dwStrType & unsupportedFlags);
200
201     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
202      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
203     if (bRet)
204     {
205         DWORD i, j, sepLen, rdnSepLen;
206         LPCSTR sep, rdnSep;
207
208         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
209             sep = semiSep;
210         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
211             sep = crlfSep;
212         else
213             sep = commaSep;
214         sepLen = strlen(sep);
215         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
216             rdnSep = spaceSep;
217         else
218             rdnSep = plusSep;
219         rdnSepLen = strlen(rdnSep);
220         for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++)
221         {
222             for (j = 0; (!psz || ret < csz) && j < info->rgRDN[i].cRDNAttr; j++)
223             {
224                 DWORD chars;
225                 char prefixBuf[10]; /* big enough for GivenName */
226                 LPCSTR prefix = NULL;
227
228                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
229                     prefix = info->rgRDN[i].rgRDNAttr[j].pszObjId;
230                 else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR)
231                 {
232                     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(
233                      CRYPT_OID_INFO_OID_KEY,
234                      info->rgRDN[i].rgRDNAttr[j].pszObjId,
235                      CRYPT_RDN_ATTR_OID_GROUP_ID);
236
237                     if (oidInfo)
238                     {
239                         WideCharToMultiByte(CP_ACP, 0, oidInfo->pwszName, -1,
240                          prefixBuf, sizeof(prefixBuf), NULL, NULL);
241                         prefix = prefixBuf;
242                     }
243                     else
244                         prefix = info->rgRDN[i].rgRDNAttr[j].pszObjId;
245                 }
246                 if (prefix)
247                 {
248                     /* - 1 is needed to account for the NULL terminator. */
249                     chars = CRYPT_AddPrefixA(prefix,
250                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
251                     ret += chars;
252                     csz -= chars;
253                 }
254                 /* FIXME: handle quoting */
255                 chars = CertRDNValueToStrA(
256                  info->rgRDN[i].rgRDNAttr[j].dwValueType, 
257                  &info->rgRDN[i].rgRDNAttr[j].Value, psz ? psz + ret : NULL,
258                  psz ? csz - ret : 0);
259                 if (chars)
260                     ret += chars - 1;
261                 if (j < info->rgRDN[i].cRDNAttr - 1)
262                 {
263                     if (psz && ret < csz - rdnSepLen - 1)
264                         memcpy(psz + ret, rdnSep, rdnSepLen);
265                     ret += rdnSepLen;
266                 }
267             }
268             if (i < info->cRDN - 1)
269             {
270                 if (psz && ret < csz - sepLen - 1)
271                     memcpy(psz + ret, sep, sepLen);
272                 ret += sepLen;
273             }
274         }
275         LocalFree(info);
276     }
277     if (psz && csz)
278     {
279         *(psz + ret) = '\0';
280         csz--;
281         ret++;
282     }
283     else
284         ret++;
285     TRACE("Returning %s\n", debugstr_a(psz));
286     return ret;
287 }
288
289 /* Adds the prefix prefix to the wide-character string pointed to by psz,
290  * followed by the character '='.  Copies no more than csz characters.  Returns
291  * the number of characters copied.  If psz is NULL, returns the number of
292  * characters that would be copied.
293  * Assumes the characters in prefix are ASCII (not multibyte characters.)
294  */
295 static DWORD CRYPT_AddPrefixAToW(LPCSTR prefix, LPWSTR psz, DWORD csz)
296 {
297     DWORD chars;
298
299     TRACE("(%s, %p, %d)\n", debugstr_a(prefix), psz, csz);
300
301     if (psz)
302     {
303         DWORD i;
304
305         chars = min(lstrlenA(prefix), csz);
306         for (i = 0; i < chars; i++)
307             *(psz + i) = prefix[i];
308         csz -= chars;
309         *(psz + chars) = '=';
310         chars++;
311         csz--;
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(lstrlenW(prefix), csz);
332         memcpy(psz, prefix, chars * sizeof(WCHAR));
333         csz -= chars;
334         *(psz + chars) = '=';
335         chars++;
336         csz--;
337     }
338     else
339         chars = lstrlenW(prefix) + 1;
340     return chars;
341 }
342
343 DWORD WINAPI CertNameToStrW(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
344  DWORD dwStrType, LPWSTR psz, DWORD csz)
345 {
346     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
347      CERT_NAME_STR_REVERSE_FLAG | 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     TRACE("(%d, %p, %08x, %p, %d)\n", dwCertEncodingType, pName, dwStrType,
358      psz, csz);
359     if (dwStrType & unsupportedFlags)
360         FIXME("unsupported flags: %08x\n", dwStrType & unsupportedFlags);
361
362     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
363      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
364     if (bRet)
365     {
366         DWORD i, j, sepLen, rdnSepLen;
367         LPCWSTR sep, rdnSep;
368
369         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
370             sep = semiSep;
371         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
372             sep = crlfSep;
373         else
374             sep = commaSep;
375         sepLen = lstrlenW(sep);
376         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
377             rdnSep = spaceSep;
378         else
379             rdnSep = plusSep;
380         rdnSepLen = lstrlenW(rdnSep);
381         for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++)
382         {
383             for (j = 0; (!psz || ret < csz) && j < info->rgRDN[i].cRDNAttr; j++)
384             {
385                 DWORD chars;
386                 LPCSTR prefixA = NULL;
387                 LPCWSTR prefixW = NULL;
388
389                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
390                     prefixA = info->rgRDN[i].rgRDNAttr[j].pszObjId;
391                 else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR)
392                 {
393                     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(
394                      CRYPT_OID_INFO_OID_KEY,
395                      info->rgRDN[i].rgRDNAttr[j].pszObjId,
396                      CRYPT_RDN_ATTR_OID_GROUP_ID);
397
398                     if (oidInfo)
399                         prefixW = oidInfo->pwszName;
400                     else
401                         prefixA = info->rgRDN[i].rgRDNAttr[j].pszObjId;
402                 }
403                 if (prefixW)
404                 {
405                     /* - 1 is needed to account for the NULL terminator. */
406                     chars = CRYPT_AddPrefixW(prefixW,
407                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
408                     ret += chars;
409                     csz -= chars;
410                 }
411                 else if (prefixA)
412                 {
413                     /* - 1 is needed to account for the NULL terminator. */
414                     chars = CRYPT_AddPrefixAToW(prefixA,
415                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
416                     ret += chars;
417                     csz -= chars;
418                 }
419                 /* FIXME: handle quoting */
420                 chars = CertRDNValueToStrW(
421                  info->rgRDN[i].rgRDNAttr[j].dwValueType, 
422                  &info->rgRDN[i].rgRDNAttr[j].Value, psz ? psz + ret : NULL,
423                  psz ? csz - ret : 0);
424                 if (chars)
425                     ret += chars - 1;
426                 if (j < info->rgRDN[i].cRDNAttr - 1)
427                 {
428                     if (psz && ret < csz - rdnSepLen - 1)
429                         memcpy(psz + ret, rdnSep, rdnSepLen * sizeof(WCHAR));
430                     ret += rdnSepLen;
431                 }
432             }
433             if (i < info->cRDN - 1)
434             {
435                 if (psz && ret < csz - sepLen - 1)
436                     memcpy(psz + ret, sep, sepLen * sizeof(WCHAR));
437                 ret += sepLen;
438             }
439         }
440         LocalFree(info);
441     }
442     if (psz && csz)
443     {
444         *(psz + ret) = '\0';
445         csz--;
446         ret++;
447     }
448     else
449         ret++;
450     TRACE("Returning %s\n", debugstr_w(psz));
451     return ret;
452 }
453
454 BOOL WINAPI CertStrToNameA(DWORD dwCertEncodingType, LPCSTR pszX500,
455  DWORD dwStrType, void *pvReserved, BYTE *pbEncoded, DWORD *pcbEncoded,
456  LPCSTR *ppszError)
457 {
458     LPWSTR x500, errorStr;
459     BOOL ret;
460     int len;
461
462     TRACE("(%08x, %s, %08x, %p, %p, %p, %p)\n", dwCertEncodingType,
463      debugstr_a(pszX500), dwStrType, pvReserved, pbEncoded, pcbEncoded,
464      ppszError);
465
466     len = MultiByteToWideChar(CP_ACP, 0, pszX500, -1, NULL, 0);
467     x500 = CryptMemAlloc(len * sizeof(WCHAR));
468     if (x500)
469     {
470         MultiByteToWideChar(CP_ACP, 0, pszX500, -1, x500, len);
471         ret = CertStrToNameW(dwCertEncodingType, x500, dwStrType, pvReserved,
472          pbEncoded, pcbEncoded, ppszError ? (LPCWSTR *)&errorStr : NULL);
473         if (ppszError)
474         {
475             DWORD i;
476
477             *ppszError = pszX500;
478             for (i = 0; i < errorStr - x500; i++)
479                 *ppszError = CharNextA(*ppszError);
480         }
481         CryptMemFree(x500);
482     }
483     else
484         ret = FALSE;
485     return ret;
486 }
487
488 struct KeynameKeeper
489 {
490     WCHAR  buf[10]; /* big enough for L"GivenName" */
491     LPWSTR keyName; /* usually = buf, but may be allocated */
492     DWORD  keyLen;
493 };
494
495 static void CRYPT_InitializeKeynameKeeper(struct KeynameKeeper *keeper)
496 {
497     keeper->keyName = keeper->buf;
498     keeper->keyLen = sizeof(keeper->buf) / sizeof(keeper->buf[0]);
499 }
500
501 static void CRYPT_FreeKeynameKeeper(struct KeynameKeeper *keeper)
502 {
503     if (keeper->keyName != keeper->buf)
504         CryptMemFree(keeper->keyName);
505 }
506
507 struct X500TokenW
508 {
509     LPCWSTR start;
510     LPCWSTR end;
511 };
512
513 static void CRYPT_KeynameKeeperFromTokenW(struct KeynameKeeper *keeper,
514  const struct X500TokenW *key)
515 {
516     DWORD len = key->end - key->start;
517
518     if (len > keeper->keyLen)
519     {
520         if (keeper->keyName == keeper->buf)
521             keeper->keyName = CryptMemAlloc(len * sizeof(WCHAR));
522         else
523             keeper->keyName = CryptMemRealloc(keeper->keyName,
524              len * sizeof(WCHAR));
525         keeper->keyLen = len;
526     }
527     memcpy(keeper->keyName, key->start, (key->end - key->start) *
528      sizeof(WCHAR));
529     keeper->keyName[len] = '\0';
530     TRACE("Keyname is %s\n", debugstr_w(keeper->keyName));
531 }
532
533 static DWORD CRYPT_GetNextKeyW(LPCWSTR str, struct X500TokenW *token,
534  LPCWSTR *ppszError)
535 {
536     DWORD ret = ERROR_SUCCESS;
537
538     while (*str && isspaceW(*str))
539         str++;
540     if (*str)
541     {
542         token->start = str;
543         while (*str && *str != '=' && !isspaceW(*str))
544             str++;
545         if (*str && (*str == '=' || isspaceW(*str)))
546             token->end = str;
547         else
548         {
549             TRACE("missing equals char at %s\n", debugstr_w(token->start));
550             if (ppszError)
551                 *ppszError = token->start;
552             ret = CRYPT_E_INVALID_X500_STRING;
553         }
554     }
555     else
556         token->start = NULL;
557     return ret;
558 }
559
560 /* Assumes separators are characters in the 0-255 range */
561 static DWORD CRYPT_GetNextValueW(LPCWSTR str, DWORD dwFlags, LPCWSTR separators,
562  struct X500TokenW *token, LPCWSTR *ppszError)
563 {
564     DWORD ret = ERROR_SUCCESS;
565
566     TRACE("(%s, %s, %p, %p)\n", debugstr_w(str), debugstr_w(separators), token,
567      ppszError);
568
569     while (*str && isspaceW(*str))
570         str++;
571     if (*str)
572     {
573         token->start = str;
574         if (!(dwFlags & CERT_NAME_STR_NO_QUOTING_FLAG) && *str == '"')
575         {
576             token->end = NULL;
577             str++;
578             while (!token->end && !ret)
579             {
580                 while (*str && *str != '"')
581                     str++;
582                 if (*str == '"')
583                 {
584                     if (*(str + 1) != '"')
585                         token->end = str + 1;
586                     else
587                         str += 2;
588                 }
589                 else
590                 {
591                     TRACE("unterminated quote at %s\n", debugstr_w(str));
592                     if (ppszError)
593                         *ppszError = str;
594                     ret = CRYPT_E_INVALID_X500_STRING;
595                 }
596             }
597         }
598         else
599         {
600             WCHAR map[256] = { 0 };
601
602             while (*separators)
603                 map[*separators++] = 1;
604             while (*str && (*str >= 0xff || !map[*(const unsigned short *)str]))
605                 str++;
606             token->end = str;
607         }
608     }
609     else
610     {
611         TRACE("missing value at %s\n", debugstr_w(str));
612         if (ppszError)
613             *ppszError = str;
614         ret = CRYPT_E_INVALID_X500_STRING;
615     }
616     return ret;
617 }
618
619 /* Encodes the string represented by value as the string type type into the
620  * CERT_NAME_BLOB output.  If there is an error and ppszError is not NULL,
621  * *ppszError is set to the first failing character.  If there is no error,
622  * output's pbData must be freed with LocalFree.
623  */
624 static BOOL CRYPT_EncodeValueWithType(DWORD dwCertEncodingType,
625  const struct X500TokenW *value, PCERT_NAME_BLOB output, DWORD type,
626  LPCWSTR *ppszError)
627 {
628     CERT_NAME_VALUE nameValue = { type, { 0, NULL } };
629     BOOL ret = FALSE;
630
631     nameValue.Value.pbData = CryptMemAlloc((value->end - value->start) *
632      sizeof(WCHAR));
633     if (nameValue.Value.pbData)
634     {
635         DWORD i;
636         LPWSTR ptr = (LPWSTR)nameValue.Value.pbData;
637
638         for (i = 0; i < value->end - value->start; i++)
639         {
640             *ptr++ = value->start[i];
641             if (value->start[i] == '"')
642                 i++;
643         }
644         nameValue.Value.cbData = (LPBYTE)ptr - nameValue.Value.pbData;
645         ret = CryptEncodeObjectEx(dwCertEncodingType, X509_UNICODE_NAME_VALUE,
646          &nameValue, CRYPT_ENCODE_ALLOC_FLAG, NULL, &output->pbData,
647          &output->cbData);
648         if (!ret && ppszError)
649         {
650             if (type == CERT_RDN_NUMERIC_STRING &&
651              GetLastError() == CRYPT_E_INVALID_NUMERIC_STRING)
652                 *ppszError = value->start + output->cbData;
653             else if (type == CERT_RDN_PRINTABLE_STRING &&
654              GetLastError() == CRYPT_E_INVALID_PRINTABLE_STRING)
655                 *ppszError = value->start + output->cbData;
656             else if (type == CERT_RDN_IA5_STRING &&
657              GetLastError() == CRYPT_E_INVALID_IA5_STRING)
658                 *ppszError = value->start + output->cbData;
659         }
660         CryptMemFree(nameValue.Value.pbData);
661     }
662     return ret;
663 }
664
665 static BOOL CRYPT_EncodeValue(DWORD dwCertEncodingType,
666  const struct X500TokenW *value, PCERT_NAME_BLOB output, const DWORD *types,
667  LPCWSTR *ppszError)
668 {
669     DWORD i;
670     BOOL ret;
671
672     ret = FALSE;
673     for (i = 0; !ret && types[i]; i++)
674         ret = CRYPT_EncodeValueWithType(dwCertEncodingType, value, output,
675          types[i], ppszError);
676     return ret;
677 }
678
679 static BOOL CRYPT_ValueToRDN(DWORD dwCertEncodingType, PCERT_NAME_INFO info,
680  PCCRYPT_OID_INFO keyOID, struct X500TokenW *value, LPCWSTR *ppszError)
681 {
682     BOOL ret = FALSE;
683
684     TRACE("OID %s, value %s\n", debugstr_a(keyOID->pszOID),
685      debugstr_wn(value->start, value->end - value->start));
686
687     if (!info->rgRDN)
688         info->rgRDN = CryptMemAlloc(sizeof(CERT_RDN));
689     else
690         info->rgRDN = CryptMemRealloc(info->rgRDN,
691          (info->cRDN + 1) * sizeof(CERT_RDN));
692     if (info->rgRDN)
693     {
694         /* FIXME: support multiple RDN attrs */
695         info->rgRDN[info->cRDN].rgRDNAttr =
696          CryptMemAlloc(sizeof(CERT_RDN_ATTR));
697         if (info->rgRDN[info->cRDN].rgRDNAttr)
698         {
699             static const DWORD defaultTypes[] = { CERT_RDN_PRINTABLE_STRING,
700              CERT_RDN_BMP_STRING, 0 };
701             const DWORD *types;
702
703             info->rgRDN[info->cRDN].cRDNAttr = 1;
704             info->rgRDN[info->cRDN].rgRDNAttr[0].pszObjId =
705              (LPSTR)keyOID->pszOID;
706             info->rgRDN[info->cRDN].rgRDNAttr[0].dwValueType =
707              CERT_RDN_ENCODED_BLOB;
708             if (keyOID->ExtraInfo.cbData)
709                 types = (const DWORD *)keyOID->ExtraInfo.pbData;
710             else
711                 types = defaultTypes;
712
713             /* Remove surrounding quotes */
714             if (value->start[0] == '"')
715             {
716                 value->start++;
717                 value->end--;
718             }
719             ret = CRYPT_EncodeValue(dwCertEncodingType, value,
720              &info->rgRDN[info->cRDN].rgRDNAttr[0].Value, types, ppszError);
721         }
722     }
723     if (ret)
724         info->cRDN++;
725     return ret;
726 }
727
728 BOOL WINAPI CertStrToNameW(DWORD dwCertEncodingType, LPCWSTR pszX500,
729  DWORD dwStrType, void *pvReserved, BYTE *pbEncoded, DWORD *pcbEncoded,
730  LPCWSTR *ppszError)
731 {
732     CERT_NAME_INFO info = { 0, NULL };
733     LPCWSTR str;
734     struct KeynameKeeper keeper;
735     DWORD i, error = ERROR_SUCCESS;
736     BOOL ret = TRUE;
737
738     TRACE("(%08x, %s, %08x, %p, %p, %p, %p)\n", dwCertEncodingType,
739      debugstr_w(pszX500), dwStrType, pvReserved, pbEncoded, pcbEncoded,
740      ppszError);
741
742     CRYPT_InitializeKeynameKeeper(&keeper);
743     str = pszX500;
744     while (str && *str && !error && ret)
745     {
746         struct X500TokenW token;
747
748         error = CRYPT_GetNextKeyW(str, &token, ppszError);
749         if (!error && token.start)
750         {
751             PCCRYPT_OID_INFO keyOID;
752
753             CRYPT_KeynameKeeperFromTokenW(&keeper, &token);
754             keyOID = CryptFindOIDInfo(CRYPT_OID_INFO_NAME_KEY, keeper.keyName,
755              CRYPT_RDN_ATTR_OID_GROUP_ID);
756             if (!keyOID)
757             {
758                 if (ppszError)
759                     *ppszError = token.start;
760                 error = CRYPT_E_INVALID_X500_STRING;
761             }
762             else
763             {
764                 str = token.end;
765                 while (isspace(*str))
766                     str++;
767                 if (*str != '=')
768                 {
769                     if (ppszError)
770                         *ppszError = str;
771                     error = CRYPT_E_INVALID_X500_STRING;
772                 }
773                 else
774                 {
775                     static const WCHAR commaSep[] = { ',',0 };
776                     static const WCHAR semiSep[] = { ';',0 };
777                     static const WCHAR crlfSep[] = { '\r','\n',0 };
778                     static const WCHAR allSeps[] = { ',',';','\r','\n',0 };
779                     LPCWSTR sep;
780
781                     str++;
782                     if (dwStrType & CERT_NAME_STR_COMMA_FLAG)
783                         sep = commaSep;
784                     else if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
785                         sep = semiSep;
786                     else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
787                         sep = crlfSep;
788                     else
789                         sep = allSeps;
790                     error = CRYPT_GetNextValueW(str, dwStrType, sep, &token,
791                      ppszError);
792                     if (!error)
793                     {
794                         str = token.end;
795                         ret = CRYPT_ValueToRDN(dwCertEncodingType, &info,
796                          keyOID, &token, ppszError);
797                     }
798                 }
799             }
800         }
801     }
802     CRYPT_FreeKeynameKeeper(&keeper);
803     if (!error)
804     {
805         ret = CryptEncodeObjectEx(dwCertEncodingType, X509_NAME, &info,
806          0, NULL, pbEncoded, pcbEncoded);
807         for (i = 0; i < info.cRDN; i++)
808         {
809             DWORD j;
810
811             for (j = 0; j < info.rgRDN[i].cRDNAttr; j++)
812                 LocalFree(info.rgRDN[i].rgRDNAttr[j].Value.pbData);
813             CryptMemFree(info.rgRDN[i].rgRDNAttr);
814         }
815         CryptMemFree(info.rgRDN);
816     }
817     else
818     {
819         SetLastError(error);
820         ret = FALSE;
821     }
822     return ret;
823 }
824
825 DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType,
826  DWORD dwFlags, void *pvTypePara, LPSTR pszNameString, DWORD cchNameString)
827 {
828     DWORD ret;
829
830     TRACE("(%p, %d, %08x, %p, %p, %d)\n", pCertContext, dwType, dwFlags,
831      pvTypePara, pszNameString, cchNameString);
832
833     if (pszNameString)
834     {
835         LPWSTR wideName;
836         DWORD nameLen;
837
838         nameLen = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
839          NULL, 0);
840         wideName = CryptMemAlloc(nameLen * sizeof(WCHAR));
841         if (wideName)
842         {
843             CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
844              wideName, nameLen);
845             nameLen = WideCharToMultiByte(CP_ACP, 0, wideName, nameLen,
846              pszNameString, cchNameString, NULL, NULL);
847             if (nameLen <= cchNameString)
848                 ret = nameLen;
849             else
850             {
851                 pszNameString[cchNameString - 1] = '\0';
852                 ret = cchNameString;
853             }
854             CryptMemFree(wideName);
855         }
856         else
857         {
858             *pszNameString = '\0';
859             ret = 1;
860         }
861     }
862     else
863         ret = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
864          NULL, 0);
865     return ret;
866 }
867
868 DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
869  DWORD dwFlags, void *pvTypePara, LPWSTR pszNameString, DWORD cchNameString)
870 {
871     DWORD ret;
872     PCERT_NAME_BLOB name;
873     LPCSTR altNameOID;
874
875     TRACE("(%p, %d, %08x, %p, %p, %d)\n", pCertContext, dwType,
876      dwFlags, pvTypePara, pszNameString, cchNameString);
877
878     if (dwFlags & CERT_NAME_ISSUER_FLAG)
879     {
880         name = &pCertContext->pCertInfo->Issuer;
881         altNameOID = szOID_ISSUER_ALT_NAME;
882     }
883     else
884     {
885         name = &pCertContext->pCertInfo->Subject;
886         altNameOID = szOID_SUBJECT_ALT_NAME;
887     }
888
889     switch (dwType)
890     {
891     case CERT_NAME_SIMPLE_DISPLAY_TYPE:
892     {
893         static const LPCSTR simpleAttributeOIDs[] = { szOID_COMMON_NAME,
894          szOID_ORGANIZATIONAL_UNIT_NAME, szOID_ORGANIZATION_NAME,
895          szOID_RSA_emailAddr };
896         CERT_NAME_INFO *info = NULL;
897         PCERT_RDN_ATTR nameAttr = NULL;
898         DWORD bytes = 0, i;
899
900         if (CryptDecodeObjectEx(pCertContext->dwCertEncodingType, X509_NAME,
901          name->pbData, name->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info,
902          &bytes))
903         {
904             for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) /
905              sizeof(simpleAttributeOIDs[0]); i++)
906                 nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], info);
907         }
908         if (!nameAttr)
909         {
910             PCERT_EXTENSION ext = CertFindExtension(altNameOID,
911              pCertContext->pCertInfo->cExtension,
912              pCertContext->pCertInfo->rgExtension);
913
914             if (ext)
915             {
916                 for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) /
917                  sizeof(simpleAttributeOIDs[0]); i++)
918                     nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], info);
919                 if (!nameAttr)
920                 {
921                     /* FIXME: gotta then look for a rfc822Name choice in ext.
922                      * Failing that, look for the first attribute.
923                      */
924                     FIXME("CERT_NAME_SIMPLE_DISPLAY_TYPE: stub\n");
925                 }
926             }
927         }
928         if (nameAttr)
929             ret = CertRDNValueToStrW(nameAttr->dwValueType, &nameAttr->Value,
930                                      pszNameString, cchNameString);
931         else
932             ret = 0;
933         if (info)
934             LocalFree(info);
935         break;
936     }
937     case CERT_NAME_FRIENDLY_DISPLAY_TYPE:
938     {
939         DWORD cch = cchNameString;
940
941         if (CertGetCertificateContextProperty(pCertContext,
942          CERT_FRIENDLY_NAME_PROP_ID, pszNameString, &cch))
943             ret = cch;
944         else
945             ret = CertGetNameStringW(pCertContext,
946              CERT_NAME_SIMPLE_DISPLAY_TYPE, dwFlags, pvTypePara, pszNameString,
947              cchNameString);
948         break;
949     }
950     default:
951         FIXME("unimplemented for type %d\n", dwType);
952         ret = 0;
953     }
954     return ret;
955 }