crypt32: Implement file stores.
[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 "wincrypt.h"
23 #include "wine/debug.h"
24
25 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
26
27 DWORD WINAPI CertRDNValueToStrA(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
28  LPSTR psz, DWORD csz)
29 {
30     DWORD ret = 0;
31
32     TRACE("(%ld, %p, %p, %ld)\n", dwValueType, pValue, psz, csz);
33
34     switch (dwValueType)
35     {
36     case CERT_RDN_ANY_TYPE:
37         break;
38     case CERT_RDN_PRINTABLE_STRING:
39     case CERT_RDN_IA5_STRING:
40         if (!psz || !csz)
41             ret = pValue->cbData;
42         else
43         {
44             DWORD chars = min(pValue->cbData, csz - 1);
45
46             if (chars)
47             {
48                 memcpy(psz, pValue->pbData, chars);
49                 ret += chars;
50                 csz -= chars;
51             }
52         }
53         break;
54     default:
55         FIXME("string type %ld unimplemented\n", dwValueType);
56     }
57     if (psz && csz)
58     {
59         *(psz + ret) = '\0';
60         csz--;
61         ret++;
62     }
63     else
64         ret++;
65     TRACE("returning %ld (%s)\n", ret, debugstr_a(psz));
66     return ret;
67 }
68
69 DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
70  LPWSTR psz, DWORD csz)
71 {
72     DWORD ret = 0;
73
74     TRACE("(%ld, %p, %p, %ld)\n", dwValueType, pValue, psz, csz);
75
76     switch (dwValueType)
77     {
78     case CERT_RDN_ANY_TYPE:
79         break;
80     case CERT_RDN_PRINTABLE_STRING:
81     case CERT_RDN_IA5_STRING:
82         if (!psz || !csz)
83             ret = pValue->cbData;
84         else
85         {
86             DWORD chars = min(pValue->cbData, csz - 1);
87
88             if (chars)
89             {
90                 DWORD i;
91
92                 for (i = 0; i < chars; i++)
93                     psz[i] = pValue->pbData[i];
94                 ret += chars;
95                 csz -= chars;
96             }
97         }
98         break;
99     default:
100         FIXME("string type %ld unimplemented\n", dwValueType);
101     }
102     if (psz && csz)
103     {
104         *(psz + ret) = '\0';
105         csz--;
106         ret++;
107     }
108     else
109         ret++;
110     TRACE("returning %ld (%s)\n", ret, debugstr_w(psz));
111     return ret;
112 }
113
114 /* Adds the prefix prefix to the string pointed to by psz, followed by the
115  * character '='.  Copies no more than csz characters.  Returns the number of
116  * characters copied.  If psz is NULL, returns the number of characters that
117  * would be copied.
118  */
119 static DWORD CRYPT_AddPrefixA(LPCSTR prefix, LPSTR psz, DWORD csz)
120 {
121     DWORD chars;
122
123     TRACE("(%s, %p, %ld)\n", debugstr_a(prefix), psz, csz);
124
125     if (psz)
126     {
127         chars = min(lstrlenA(prefix), csz);
128         memcpy(psz, prefix, chars);
129         csz -= chars;
130         *(psz + chars) = '=';
131         chars++;
132         csz--;
133     }
134     else
135         chars = lstrlenA(prefix) + 1;
136     return chars;
137 }
138
139 DWORD WINAPI CertNameToStrA(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
140  DWORD dwStrType, LPSTR psz, DWORD csz)
141 {
142     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
143      CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
144     static const char commaSep[] = ", ";
145     static const char semiSep[] = "; ";
146     static const char crlfSep[] = "\r\n";
147     static const char plusSep[] = " + ";
148     static const char spaceSep[] = " ";
149     DWORD ret = 0, bytes = 0;
150     BOOL bRet;
151     CERT_NAME_INFO *info;
152
153     TRACE("(%ld, %p, %08lx, %p, %ld)\n", dwCertEncodingType, pName, dwStrType,
154      psz, csz);
155     if (dwStrType & unsupportedFlags)
156         FIXME("unsupported flags: %08lx\n", dwStrType & unsupportedFlags);
157
158     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
159      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
160     if (bRet)
161     {
162         DWORD i, j, sepLen, rdnSepLen;
163         LPCSTR sep, rdnSep;
164
165         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
166             sep = semiSep;
167         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
168             sep = crlfSep;
169         else
170             sep = commaSep;
171         sepLen = strlen(sep);
172         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
173             rdnSep = spaceSep;
174         else
175             rdnSep = plusSep;
176         rdnSepLen = strlen(rdnSep);
177         for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++)
178         {
179             for (j = 0; (!psz || ret < csz) && j < info->rgRDN[i].cRDNAttr; j++)
180             {
181                 DWORD chars;
182                 char prefixBuf[10]; /* big enough for GivenName */
183                 LPCSTR prefix = NULL;
184
185                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
186                     prefix = info->rgRDN[i].rgRDNAttr[j].pszObjId;
187                 else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR)
188                 {
189                     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(
190                      CRYPT_OID_INFO_OID_KEY,
191                      info->rgRDN[i].rgRDNAttr[j].pszObjId,
192                      CRYPT_RDN_ATTR_OID_GROUP_ID);
193
194                     if (oidInfo)
195                     {
196                         WideCharToMultiByte(CP_ACP, 0, oidInfo->pwszName, -1,
197                          prefixBuf, sizeof(prefixBuf), NULL, NULL);
198                         prefix = prefixBuf;
199                     }
200                     else
201                         prefix = info->rgRDN[i].rgRDNAttr[j].pszObjId;
202                 }
203                 if (prefix)
204                 {
205                     /* - 1 is needed to account for the NULL terminator. */
206                     chars = CRYPT_AddPrefixA(prefix,
207                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
208                     ret += chars;
209                     csz -= chars;
210                 }
211                 /* FIXME: handle quoting */
212                 chars = CertRDNValueToStrA(
213                  info->rgRDN[i].rgRDNAttr[j].dwValueType, 
214                  &info->rgRDN[i].rgRDNAttr[j].Value, psz ? psz + ret : NULL,
215                  psz ? csz - ret : 0);
216                 if (chars)
217                     ret += chars - 1;
218                 if (j < info->rgRDN[i].cRDNAttr - 1)
219                 {
220                     if (psz && ret < csz - rdnSepLen - 1)
221                         memcpy(psz + ret, rdnSep, rdnSepLen);
222                     ret += rdnSepLen;
223                 }
224             }
225             if (i < info->cRDN - 1)
226             {
227                 if (psz && ret < csz - sepLen - 1)
228                     memcpy(psz + ret, sep, sepLen);
229                 ret += sepLen;
230             }
231         }
232         LocalFree(info);
233     }
234     if (psz && csz)
235     {
236         *(psz + ret) = '\0';
237         csz--;
238         ret++;
239     }
240     else
241         ret++;
242     TRACE("Returning %s\n", debugstr_a(psz));
243     return ret;
244 }
245
246 /* Adds the prefix prefix to the wide-character string pointed to by psz,
247  * followed by the character '='.  Copies no more than csz characters.  Returns
248  * the number of characters copied.  If psz is NULL, returns the number of
249  * characters that would be copied.
250  * Assumes the characters in prefix are ASCII (not multibyte characters.)
251  */
252 static DWORD CRYPT_AddPrefixAToW(LPCSTR prefix, LPWSTR psz, DWORD csz)
253 {
254     DWORD chars;
255
256     TRACE("(%s, %p, %ld)\n", debugstr_a(prefix), psz, csz);
257
258     if (psz)
259     {
260         DWORD i;
261
262         chars = min(lstrlenA(prefix), csz);
263         for (i = 0; i < chars; i++)
264             *(psz + i) = prefix[i];
265         csz -= chars;
266         *(psz + chars) = '=';
267         chars++;
268         csz--;
269     }
270     else
271         chars = lstrlenA(prefix) + 1;
272     return chars;
273 }
274
275 /* Adds the prefix prefix to the string pointed to by psz, followed by the
276  * character '='.  Copies no more than csz characters.  Returns the number of
277  * characters copied.  If psz is NULL, returns the number of characters that
278  * would be copied.
279  */
280 static DWORD CRYPT_AddPrefixW(LPCWSTR prefix, LPWSTR psz, DWORD csz)
281 {
282     DWORD chars;
283
284     TRACE("(%s, %p, %ld)\n", debugstr_w(prefix), psz, csz);
285
286     if (psz)
287     {
288         chars = min(lstrlenW(prefix), csz);
289         memcpy(psz, prefix, chars * sizeof(WCHAR));
290         csz -= chars;
291         *(psz + chars) = '=';
292         chars++;
293         csz--;
294     }
295     else
296         chars = lstrlenW(prefix) + 1;
297     return chars;
298 }
299
300 DWORD WINAPI CertNameToStrW(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
301  DWORD dwStrType, LPWSTR psz, DWORD csz)
302 {
303     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
304      CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
305     static const WCHAR commaSep[] = { ',',' ',0 };
306     static const WCHAR semiSep[] = { ';',' ',0 };
307     static const WCHAR crlfSep[] = { '\r','\n',0 };
308     static const WCHAR plusSep[] = { ' ','+',' ',0 };
309     static const WCHAR spaceSep[] = { ' ',0 };
310     DWORD ret = 0, bytes = 0;
311     BOOL bRet;
312     CERT_NAME_INFO *info;
313
314     TRACE("(%ld, %p, %08lx, %p, %ld)\n", dwCertEncodingType, pName, dwStrType,
315      psz, csz);
316     if (dwStrType & unsupportedFlags)
317         FIXME("unsupported flags: %08lx\n", dwStrType & unsupportedFlags);
318
319     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
320      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
321     if (bRet)
322     {
323         DWORD i, j, sepLen, rdnSepLen;
324         LPCWSTR sep, rdnSep;
325
326         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
327             sep = semiSep;
328         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
329             sep = crlfSep;
330         else
331             sep = commaSep;
332         sepLen = lstrlenW(sep);
333         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
334             rdnSep = spaceSep;
335         else
336             rdnSep = plusSep;
337         rdnSepLen = lstrlenW(rdnSep);
338         for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++)
339         {
340             for (j = 0; (!psz || ret < csz) && j < info->rgRDN[i].cRDNAttr; j++)
341             {
342                 DWORD chars;
343                 LPCSTR prefixA = NULL;
344                 LPCWSTR prefixW = NULL;
345
346                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
347                     prefixA = info->rgRDN[i].rgRDNAttr[j].pszObjId;
348                 else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR)
349                 {
350                     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(
351                      CRYPT_OID_INFO_OID_KEY,
352                      info->rgRDN[i].rgRDNAttr[j].pszObjId,
353                      CRYPT_RDN_ATTR_OID_GROUP_ID);
354
355                     if (oidInfo)
356                         prefixW = oidInfo->pwszName;
357                     else
358                         prefixA = info->rgRDN[i].rgRDNAttr[j].pszObjId;
359                 }
360                 if (prefixW)
361                 {
362                     /* - 1 is needed to account for the NULL terminator. */
363                     chars = CRYPT_AddPrefixW(prefixW,
364                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
365                     ret += chars;
366                     csz -= chars;
367                 }
368                 else if (prefixA)
369                 {
370                     /* - 1 is needed to account for the NULL terminator. */
371                     chars = CRYPT_AddPrefixAToW(prefixA,
372                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
373                     ret += chars;
374                     csz -= chars;
375                 }
376                 /* FIXME: handle quoting */
377                 chars = CertRDNValueToStrW(
378                  info->rgRDN[i].rgRDNAttr[j].dwValueType, 
379                  &info->rgRDN[i].rgRDNAttr[j].Value, psz ? psz + ret : NULL,
380                  psz ? csz - ret : 0);
381                 if (chars)
382                     ret += chars - 1;
383                 if (j < info->rgRDN[i].cRDNAttr - 1)
384                 {
385                     if (psz && ret < csz - rdnSepLen - 1)
386                         memcpy(psz + ret, rdnSep, rdnSepLen * sizeof(WCHAR));
387                     ret += rdnSepLen;
388                 }
389             }
390             if (i < info->cRDN - 1)
391             {
392                 if (psz && ret < csz - sepLen - 1)
393                     memcpy(psz + ret, sep, sepLen * sizeof(WCHAR));
394                 ret += sepLen;
395             }
396         }
397         LocalFree(info);
398     }
399     if (psz && csz)
400     {
401         *(psz + ret) = '\0';
402         csz--;
403         ret++;
404     }
405     else
406         ret++;
407     TRACE("Returning %s\n", debugstr_w(psz));
408     return ret;
409 }
410
411 DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType,
412  DWORD dwFlags, void *pvTypePara, LPSTR pszNameString, DWORD cchNameString)
413 {
414     DWORD ret;
415
416     TRACE("(%p, %ld, %08lx, %p, %p, %ld)\n", pCertContext, dwType, dwFlags,
417      pvTypePara, pszNameString, cchNameString);
418
419     if (pszNameString)
420     {
421         LPWSTR wideName;
422         DWORD nameLen;
423
424         nameLen = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
425          NULL, 0);
426         wideName = CryptMemAlloc(nameLen * sizeof(WCHAR));
427         if (wideName)
428         {
429             CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
430              wideName, nameLen);
431             nameLen = WideCharToMultiByte(CP_ACP, 0, wideName, nameLen,
432              pszNameString, cchNameString, NULL, NULL);
433             if (nameLen <= cchNameString)
434                 ret = nameLen;
435             else
436             {
437                 pszNameString[cchNameString - 1] = '\0';
438                 ret = cchNameString;
439             }
440             CryptMemFree(wideName);
441         }
442         else
443         {
444             *pszNameString = '\0';
445             ret = 1;
446         }
447     }
448     else
449         ret = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
450          NULL, 0);
451     return ret;
452 }
453
454 DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
455  DWORD dwFlags, void *pvTypePara, LPWSTR pszNameString, DWORD cchNameString)
456 {
457     DWORD ret;
458     PCERT_NAME_BLOB name;
459     LPCSTR altNameOID;
460
461     TRACE("(%p, %ld, %08lx, %p, %p, %ld)\n", pCertContext, dwType,
462      dwFlags, pvTypePara, pszNameString, cchNameString);
463
464     if (dwFlags & CERT_NAME_ISSUER_FLAG)
465     {
466         name = &pCertContext->pCertInfo->Issuer;
467         altNameOID = szOID_ISSUER_ALT_NAME;
468     }
469     else
470     {
471         name = &pCertContext->pCertInfo->Subject;
472         altNameOID = szOID_SUBJECT_ALT_NAME;
473     }
474
475     switch (dwType)
476     {
477     case CERT_NAME_SIMPLE_DISPLAY_TYPE:
478     {
479         static const LPCSTR simpleAttributeOIDs[] = { szOID_COMMON_NAME,
480          szOID_ORGANIZATIONAL_UNIT_NAME, szOID_ORGANIZATION_NAME,
481          szOID_RSA_emailAddr };
482         CERT_NAME_INFO *info = NULL;
483         PCERT_RDN_ATTR nameAttr = NULL;
484         DWORD bytes = 0, i;
485
486         if (CryptDecodeObjectEx(pCertContext->dwCertEncodingType, X509_NAME,
487          name->pbData, name->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info,
488          &bytes))
489         {
490             for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) /
491              sizeof(simpleAttributeOIDs[0]); i++)
492                 nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], info);
493         }
494         else
495             ret = 0;
496         if (!nameAttr)
497         {
498             PCERT_EXTENSION ext = CertFindExtension(altNameOID,
499              pCertContext->pCertInfo->cExtension,
500              pCertContext->pCertInfo->rgExtension);
501
502             if (ext)
503             {
504                 for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) /
505                  sizeof(simpleAttributeOIDs[0]); i++)
506                     nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], info);
507                 if (!nameAttr)
508                 {
509                     /* FIXME: gotta then look for a rfc822Name choice in ext.
510                      * Failing that, look for the first attribute.
511                      */
512                     FIXME("CERT_NAME_SIMPLE_DISPLAY_TYPE: stub\n");
513                     ret = 0;
514                 }
515             }
516         }
517         ret = CertRDNValueToStrW(nameAttr->dwValueType, &nameAttr->Value,
518          pszNameString, cchNameString);
519         if (info)
520             LocalFree(info);
521         break;
522     }
523     case CERT_NAME_FRIENDLY_DISPLAY_TYPE:
524     {
525         DWORD cch = cchNameString;
526
527         if (CertGetCertificateContextProperty(pCertContext,
528          CERT_FRIENDLY_NAME_PROP_ID, pszNameString, &cch))
529             ret = cch;
530         else
531             ret = CertGetNameStringW(pCertContext,
532              CERT_NAME_SIMPLE_DISPLAY_TYPE, dwFlags, pvTypePara, pszNameString,
533              cchNameString);
534         break;
535     }
536     default:
537         FIXME("unimplemented for type %ld\n", dwType);
538         ret = 0;
539     }
540     return ret;
541 }