crypt32: Fixed day/month mismatch in time encoding routines.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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     return ret;
66 }
67
68 DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
69  LPWSTR psz, DWORD csz)
70 {
71     DWORD ret = 0;
72
73     TRACE("(%ld, %p, %p, %ld)\n", dwValueType, pValue, psz, csz);
74
75     switch (dwValueType)
76     {
77     case CERT_RDN_ANY_TYPE:
78         break;
79     case CERT_RDN_PRINTABLE_STRING:
80     case CERT_RDN_IA5_STRING:
81         if (!psz || !csz)
82             ret = pValue->cbData;
83         else
84         {
85             DWORD chars = min(pValue->cbData, csz - 1);
86
87             if (chars)
88             {
89                 DWORD i;
90
91                 for (i = 0; i < chars; i++)
92                     psz[i] = pValue->pbData[i];
93                 ret += chars;
94                 csz -= chars;
95             }
96         }
97         break;
98     default:
99         FIXME("string type %ld unimplemented\n", dwValueType);
100     }
101     if (psz && csz)
102     {
103         *(psz + ret) = '\0';
104         csz--;
105         ret++;
106     }
107     else
108         ret++;
109     return ret;
110 }
111
112 DWORD WINAPI CertNameToStrA(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
113  DWORD dwStrType, LPSTR psz, DWORD csz)
114 {
115     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
116      CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
117     static const char commaSep[] = ", ";
118     static const char semiSep[] = "; ";
119     static const char crlfSep[] = "\r\n";
120     static const char plusSep[] = " + ";
121     static const char spaceSep[] = " ";
122     DWORD ret = 0, bytes = 0;
123     BOOL bRet;
124     CERT_NAME_INFO *info;
125
126     TRACE("(%ld, %p, %08lx, %p, %ld)\n", dwCertEncodingType, pName, dwStrType,
127      psz, csz);
128     if (dwStrType & unsupportedFlags)
129         FIXME("unsupported flags: %08lx\n", dwStrType & unsupportedFlags);
130
131     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
132      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
133     if (bRet)
134     {
135         DWORD i, j, sepLen, rdnSepLen;
136         LPCSTR sep, rdnSep;
137
138         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
139             sep = semiSep;
140         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
141             sep = crlfSep;
142         else
143             sep = commaSep;
144         sepLen = strlen(sep);
145         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
146             rdnSep = spaceSep;
147         else
148             rdnSep = plusSep;
149         rdnSepLen = strlen(rdnSep);
150         for (i = 0; ret < csz && i < info->cRDN; i++)
151         {
152             for (j = 0; ret < csz && j < info->rgRDN[i].cRDNAttr; j++)
153             {
154                 DWORD chars;
155
156                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
157                 {
158                     /* - 1 is needed to account for the NULL terminator. */
159                     chars = min(
160                      lstrlenA(info->rgRDN[i].rgRDNAttr[j].pszObjId),
161                      csz - ret - 1);
162                     if (psz && chars)
163                         memcpy(psz + ret, info->rgRDN[i].rgRDNAttr[j].pszObjId,
164                          chars);
165                     ret += chars;
166                     csz -= chars;
167                     if (csz > 1)
168                     {
169                         if (psz)
170                             *(psz + ret) = '=';
171                         ret++;
172                         csz--;
173                     }
174                 }
175                 /* FIXME: handle quoting */
176                 chars = CertRDNValueToStrA(
177                  info->rgRDN[i].rgRDNAttr[j].dwValueType, 
178                  &info->rgRDN[i].rgRDNAttr[j].Value, psz ? psz + ret : NULL,
179                  csz - ret - 1);
180                 if (chars)
181                     ret += chars - 1;
182                 if (j < info->rgRDN[i].cRDNAttr - 1)
183                 {
184                     if (psz && ret < csz - rdnSepLen - 1)
185                         memcpy(psz + ret, rdnSep, rdnSepLen);
186                     ret += rdnSepLen;
187                 }
188             }
189             if (i < info->cRDN - 1)
190             {
191                 if (psz && ret < csz - sepLen - 1)
192                     memcpy(psz + ret, sep, sepLen);
193                 ret += sepLen;
194             }
195         }
196         LocalFree(info);
197     }
198     if (psz && csz)
199     {
200         *(psz + ret) = '\0';
201         csz--;
202         ret++;
203     }
204     else
205         ret++;
206     return ret;
207 }
208
209 DWORD WINAPI CertNameToStrW(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
210  DWORD dwStrType, LPWSTR psz, DWORD csz)
211 {
212     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
213      CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
214     static const WCHAR commaSep[] = { ',',' ',0 };
215     static const WCHAR semiSep[] = { ';',' ',0 };
216     static const WCHAR crlfSep[] = { '\r','\n',0 };
217     static const WCHAR plusSep[] = { ' ','+',' ',0 };
218     static const WCHAR spaceSep[] = { ' ',0 };
219     DWORD ret = 0, bytes = 0;
220     BOOL bRet;
221     CERT_NAME_INFO *info;
222
223     TRACE("(%ld, %p, %08lx, %p, %ld)\n", dwCertEncodingType, pName, dwStrType,
224      psz, csz);
225     if (dwStrType & unsupportedFlags)
226         FIXME("unsupported flags: %08lx\n", dwStrType & unsupportedFlags);
227
228     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
229      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
230     if (bRet)
231     {
232         DWORD i, j, sepLen, rdnSepLen;
233         LPCWSTR sep, rdnSep;
234
235         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
236             sep = semiSep;
237         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
238             sep = crlfSep;
239         else
240             sep = commaSep;
241         sepLen = lstrlenW(sep);
242         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
243             rdnSep = spaceSep;
244         else
245             rdnSep = plusSep;
246         rdnSepLen = lstrlenW(rdnSep);
247         for (i = 0; ret < csz && i < info->cRDN; i++)
248         {
249             for (j = 0; ret < csz && j < info->rgRDN[i].cRDNAttr; j++)
250             {
251                 DWORD chars;
252
253                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
254                 {
255                     /* - 1 is needed to account for the NULL terminator. */
256                     chars = min(
257                      lstrlenA(info->rgRDN[i].rgRDNAttr[j].pszObjId),
258                      csz - ret - 1);
259                     if (psz && chars)
260                     {
261                         DWORD k;
262
263                         for (k = 0; k < chars; k++)
264                             *(psz + ret + k) =
265                              info->rgRDN[i].rgRDNAttr[j].pszObjId[k];
266                     }
267                     ret += chars;
268                     csz -= chars;
269                     if (csz > 1)
270                     {
271                         if (psz)
272                             *(psz + ret) = '=';
273                         ret++;
274                         csz--;
275                     }
276                 }
277                 /* FIXME: handle quoting */
278                 chars = CertRDNValueToStrW(
279                  info->rgRDN[i].rgRDNAttr[j].dwValueType, 
280                  &info->rgRDN[i].rgRDNAttr[j].Value, psz ? psz + ret : NULL,
281                  csz - ret - 1);
282                 if (chars)
283                     ret += chars - 1;
284                 if (j < info->rgRDN[i].cRDNAttr - 1)
285                 {
286                     if (psz && ret < csz - rdnSepLen - 1)
287                         memcpy(psz + ret, rdnSep, rdnSepLen * sizeof(WCHAR));
288                     ret += rdnSepLen;
289                 }
290             }
291             if (i < info->cRDN - 1)
292             {
293                 if (psz && ret < csz - sepLen - 1)
294                     memcpy(psz + ret, sep, sepLen * sizeof(WCHAR));
295                 ret += sepLen;
296             }
297         }
298         LocalFree(info);
299     }
300     if (psz && csz)
301     {
302         *(psz + ret) = '\0';
303         csz--;
304         ret++;
305     }
306     else
307         ret++;
308     return ret;
309 }
310
311 DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType,
312  DWORD dwFlags, void *pvTypePara, LPSTR pszNameString, DWORD cchNameString)
313 {
314     DWORD ret;
315
316     TRACE("(%p, %ld, %08lx, %p, %p, %ld)\n", pCertContext, dwType, dwFlags,
317      pvTypePara, pszNameString, cchNameString);
318
319     if (pszNameString)
320     {
321         LPWSTR wideName;
322         DWORD nameLen;
323
324         nameLen = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
325          NULL, 0);
326         wideName = CryptMemAlloc(nameLen * sizeof(WCHAR));
327         if (wideName)
328         {
329             CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
330              wideName, nameLen);
331             nameLen = WideCharToMultiByte(CP_ACP, 0, wideName, nameLen,
332              pszNameString, cchNameString, NULL, NULL);
333             if (nameLen <= cchNameString)
334                 ret = nameLen;
335             else
336             {
337                 pszNameString[cchNameString - 1] = '\0';
338                 ret = cchNameString;
339             }
340             CryptMemFree(wideName);
341         }
342         else
343         {
344             *pszNameString = '\0';
345             ret = 1;
346         }
347     }
348     else
349         ret = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
350          NULL, 0);
351     return ret;
352 }
353
354 DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
355  DWORD dwFlags, void *pvTypePara, LPWSTR pszNameString, DWORD cchNameString)
356 {
357     DWORD ret;
358     PCERT_NAME_BLOB name;
359     LPCSTR altNameOID;
360
361     TRACE("(%p, %ld, %08lx, %p, %p, %ld)\n", pCertContext, dwType,
362      dwFlags, pvTypePara, pszNameString, cchNameString);
363
364     if (dwFlags & CERT_NAME_ISSUER_FLAG)
365     {
366         name = &pCertContext->pCertInfo->Issuer;
367         altNameOID = szOID_ISSUER_ALT_NAME;
368     }
369     else
370     {
371         name = &pCertContext->pCertInfo->Subject;
372         altNameOID = szOID_SUBJECT_ALT_NAME;
373     }
374
375     switch (dwType)
376     {
377     case CERT_NAME_SIMPLE_DISPLAY_TYPE:
378     {
379         static const LPCSTR simpleAttributeOIDs[] = { szOID_COMMON_NAME,
380          szOID_ORGANIZATIONAL_UNIT_NAME, szOID_ORGANIZATION_NAME,
381          szOID_RSA_emailAddr };
382         CERT_NAME_INFO *info = NULL;
383         PCERT_RDN_ATTR nameAttr = NULL;
384         DWORD bytes = 0, i;
385
386         if (CryptDecodeObjectEx(pCertContext->dwCertEncodingType, X509_NAME,
387          name->pbData, name->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info,
388          &bytes))
389         {
390             for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) /
391              sizeof(simpleAttributeOIDs[0]); i++)
392                 nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], info);
393         }
394         else
395             ret = 0;
396         if (!nameAttr)
397         {
398             PCERT_EXTENSION ext = CertFindExtension(altNameOID,
399              pCertContext->pCertInfo->cExtension,
400              pCertContext->pCertInfo->rgExtension);
401
402             if (ext)
403             {
404                 for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) /
405                  sizeof(simpleAttributeOIDs[0]); i++)
406                     nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], info);
407                 if (!nameAttr)
408                 {
409                     /* FIXME: gotta then look for a rfc822Name choice in ext.
410                      * Failing that, look for the first attribute.
411                      */
412                     FIXME("CERT_NAME_SIMPLE_DISPLAY_TYPE: stub\n");
413                     ret = 0;
414                 }
415             }
416         }
417         ret = CertRDNValueToStrW(nameAttr->dwValueType, &nameAttr->Value,
418          pszNameString, cchNameString);
419         if (info)
420             LocalFree(info);
421         break;
422     }
423     case CERT_NAME_FRIENDLY_DISPLAY_TYPE:
424     {
425         DWORD cch = cchNameString;
426
427         if (CertGetCertificateContextProperty(pCertContext,
428          CERT_FRIENDLY_NAME_PROP_ID, pszNameString, &cch))
429             ret = cch;
430         else
431             ret = CertGetNameStringW(pCertContext,
432              CERT_NAME_SIMPLE_DISPLAY_TYPE, dwFlags, pvTypePara, pszNameString,
433              cchNameString);
434         break;
435     }
436     default:
437         FIXME("unimplemented for type %ld\n", dwType);
438         ret = 0;
439     }
440     return ret;
441 }