| /* | 
 |  * Copyright 2006 Juan Lang for CodeWeavers | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, write to the Free Software | 
 |  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
 |  */ | 
 | #include <stdarg.h> | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "winnls.h" | 
 | #include "winuser.h" | 
 | #include "wincrypt.h" | 
 | #include "wine/debug.h" | 
 | #include "wine/unicode.h" | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL(crypt); | 
 |  | 
 | DWORD WINAPI CertRDNValueToStrA(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue, | 
 |  LPSTR psz, DWORD csz) | 
 | { | 
 |     DWORD ret = 0; | 
 |  | 
 |     TRACE("(%d, %p, %p, %d)\n", dwValueType, pValue, psz, csz); | 
 |  | 
 |     switch (dwValueType) | 
 |     { | 
 |     case CERT_RDN_ANY_TYPE: | 
 |         break; | 
 |     case CERT_RDN_NUMERIC_STRING: | 
 |     case CERT_RDN_PRINTABLE_STRING: | 
 |     case CERT_RDN_TELETEX_STRING: | 
 |     case CERT_RDN_VIDEOTEX_STRING: | 
 |     case CERT_RDN_IA5_STRING: | 
 |     case CERT_RDN_GRAPHIC_STRING: | 
 |     case CERT_RDN_VISIBLE_STRING: | 
 |     case CERT_RDN_GENERAL_STRING: | 
 |         if (!psz || !csz) | 
 |             ret = pValue->cbData; | 
 |         else | 
 |         { | 
 |             DWORD chars = min(pValue->cbData, csz - 1); | 
 |  | 
 |             if (chars) | 
 |             { | 
 |                 memcpy(psz, pValue->pbData, chars); | 
 |                 ret += chars; | 
 |                 csz -= chars; | 
 |             } | 
 |         } | 
 |         break; | 
 |     case CERT_RDN_UTF8_STRING: | 
 |         if (!psz || !csz) | 
 |             ret = WideCharToMultiByte(CP_UTF8, 0, (LPWSTR)pValue->pbData, | 
 |              pValue->cbData / sizeof(WCHAR) + 1, NULL, 0, NULL, NULL); | 
 |         else | 
 |         { | 
 |             ret = WideCharToMultiByte(CP_UTF8, 0, (LPWSTR)pValue->pbData, | 
 |              pValue->cbData / sizeof(WCHAR) + 1, psz, csz - 1, NULL, NULL); | 
 |             csz -= ret; | 
 |         } | 
 |         break; | 
 |     default: | 
 |         FIXME("string type %d unimplemented\n", dwValueType); | 
 |     } | 
 |     if (psz && csz) | 
 |     { | 
 |         *(psz + ret) = '\0'; | 
 |         csz--; | 
 |         ret++; | 
 |     } | 
 |     else | 
 |         ret++; | 
 |     TRACE("returning %d (%s)\n", ret, debugstr_a(psz)); | 
 |     return ret; | 
 | } | 
 |  | 
 | DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue, | 
 |  LPWSTR psz, DWORD csz) | 
 | { | 
 |     DWORD ret = 0; | 
 |  | 
 |     TRACE("(%d, %p, %p, %d)\n", dwValueType, pValue, psz, csz); | 
 |  | 
 |     switch (dwValueType) | 
 |     { | 
 |     case CERT_RDN_ANY_TYPE: | 
 |         break; | 
 |     case CERT_RDN_NUMERIC_STRING: | 
 |     case CERT_RDN_PRINTABLE_STRING: | 
 |     case CERT_RDN_TELETEX_STRING: | 
 |     case CERT_RDN_VIDEOTEX_STRING: | 
 |     case CERT_RDN_IA5_STRING: | 
 |     case CERT_RDN_GRAPHIC_STRING: | 
 |     case CERT_RDN_VISIBLE_STRING: | 
 |     case CERT_RDN_GENERAL_STRING: | 
 |         if (!psz || !csz) | 
 |             ret = pValue->cbData; | 
 |         else | 
 |         { | 
 |             DWORD chars = min(pValue->cbData, csz - 1); | 
 |  | 
 |             if (chars) | 
 |             { | 
 |                 DWORD i; | 
 |  | 
 |                 for (i = 0; i < chars; i++) | 
 |                     psz[i] = pValue->pbData[i]; | 
 |                 ret += chars; | 
 |                 csz -= chars; | 
 |             } | 
 |         } | 
 |         break; | 
 |     case CERT_RDN_UTF8_STRING: | 
 |         if (!psz || !csz) | 
 |             ret = pValue->cbData / sizeof(WCHAR); | 
 |         else | 
 |         { | 
 |             DWORD chars = min(pValue->cbData / sizeof(WCHAR), csz - 1); | 
 |  | 
 |             if (chars) | 
 |             { | 
 |                 DWORD i; | 
 |  | 
 |                 for (i = 0; i < chars; i++) | 
 |                     psz[i] = *((LPWSTR)pValue->pbData + i); | 
 |                 ret += chars; | 
 |                 csz -= chars; | 
 |             } | 
 |         } | 
 |         break; | 
 |     default: | 
 |         FIXME("string type %d unimplemented\n", dwValueType); | 
 |     } | 
 |     if (psz && csz) | 
 |     { | 
 |         *(psz + ret) = '\0'; | 
 |         csz--; | 
 |         ret++; | 
 |     } | 
 |     else | 
 |         ret++; | 
 |     TRACE("returning %d (%s)\n", ret, debugstr_w(psz)); | 
 |     return ret; | 
 | } | 
 |  | 
 | /* Adds the prefix prefix to the string pointed to by psz, followed by the | 
 |  * character '='.  Copies no more than csz characters.  Returns the number of | 
 |  * characters copied.  If psz is NULL, returns the number of characters that | 
 |  * would be copied. | 
 |  */ | 
 | static DWORD CRYPT_AddPrefixA(LPCSTR prefix, LPSTR psz, DWORD csz) | 
 | { | 
 |     DWORD chars; | 
 |  | 
 |     TRACE("(%s, %p, %d)\n", debugstr_a(prefix), psz, csz); | 
 |  | 
 |     if (psz) | 
 |     { | 
 |         chars = min(lstrlenA(prefix), csz); | 
 |         memcpy(psz, prefix, chars); | 
 |         csz -= chars; | 
 |         *(psz + chars) = '='; | 
 |         chars++; | 
 |         csz--; | 
 |     } | 
 |     else | 
 |         chars = lstrlenA(prefix) + 1; | 
 |     return chars; | 
 | } | 
 |  | 
 | DWORD WINAPI CertNameToStrA(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName, | 
 |  DWORD dwStrType, LPSTR psz, DWORD csz) | 
 | { | 
 |     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG | | 
 |      CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG; | 
 |     static const char commaSep[] = ", "; | 
 |     static const char semiSep[] = "; "; | 
 |     static const char crlfSep[] = "\r\n"; | 
 |     static const char plusSep[] = " + "; | 
 |     static const char spaceSep[] = " "; | 
 |     DWORD ret = 0, bytes = 0; | 
 |     BOOL bRet; | 
 |     CERT_NAME_INFO *info; | 
 |  | 
 |     TRACE("(%d, %p, %08x, %p, %d)\n", dwCertEncodingType, pName, dwStrType, | 
 |      psz, csz); | 
 |     if (dwStrType & unsupportedFlags) | 
 |         FIXME("unsupported flags: %08x\n", dwStrType & unsupportedFlags); | 
 |  | 
 |     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData, | 
 |      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes); | 
 |     if (bRet) | 
 |     { | 
 |         DWORD i, j, sepLen, rdnSepLen; | 
 |         LPCSTR sep, rdnSep; | 
 |  | 
 |         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG) | 
 |             sep = semiSep; | 
 |         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG) | 
 |             sep = crlfSep; | 
 |         else | 
 |             sep = commaSep; | 
 |         sepLen = strlen(sep); | 
 |         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG) | 
 |             rdnSep = spaceSep; | 
 |         else | 
 |             rdnSep = plusSep; | 
 |         rdnSepLen = strlen(rdnSep); | 
 |         for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++) | 
 |         { | 
 |             for (j = 0; (!psz || ret < csz) && j < info->rgRDN[i].cRDNAttr; j++) | 
 |             { | 
 |                 DWORD chars; | 
 |                 char prefixBuf[10]; /* big enough for GivenName */ | 
 |                 LPCSTR prefix = NULL; | 
 |  | 
 |                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR) | 
 |                     prefix = info->rgRDN[i].rgRDNAttr[j].pszObjId; | 
 |                 else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR) | 
 |                 { | 
 |                     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo( | 
 |                      CRYPT_OID_INFO_OID_KEY, | 
 |                      info->rgRDN[i].rgRDNAttr[j].pszObjId, | 
 |                      CRYPT_RDN_ATTR_OID_GROUP_ID); | 
 |  | 
 |                     if (oidInfo) | 
 |                     { | 
 |                         WideCharToMultiByte(CP_ACP, 0, oidInfo->pwszName, -1, | 
 |                          prefixBuf, sizeof(prefixBuf), NULL, NULL); | 
 |                         prefix = prefixBuf; | 
 |                     } | 
 |                     else | 
 |                         prefix = info->rgRDN[i].rgRDNAttr[j].pszObjId; | 
 |                 } | 
 |                 if (prefix) | 
 |                 { | 
 |                     /* - 1 is needed to account for the NULL terminator. */ | 
 |                     chars = CRYPT_AddPrefixA(prefix, | 
 |                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0); | 
 |                     ret += chars; | 
 |                     csz -= chars; | 
 |                 } | 
 |                 /* FIXME: handle quoting */ | 
 |                 chars = CertRDNValueToStrA( | 
 |                  info->rgRDN[i].rgRDNAttr[j].dwValueType,  | 
 |                  &info->rgRDN[i].rgRDNAttr[j].Value, psz ? psz + ret : NULL, | 
 |                  psz ? csz - ret : 0); | 
 |                 if (chars) | 
 |                     ret += chars - 1; | 
 |                 if (j < info->rgRDN[i].cRDNAttr - 1) | 
 |                 { | 
 |                     if (psz && ret < csz - rdnSepLen - 1) | 
 |                         memcpy(psz + ret, rdnSep, rdnSepLen); | 
 |                     ret += rdnSepLen; | 
 |                 } | 
 |             } | 
 |             if (i < info->cRDN - 1) | 
 |             { | 
 |                 if (psz && ret < csz - sepLen - 1) | 
 |                     memcpy(psz + ret, sep, sepLen); | 
 |                 ret += sepLen; | 
 |             } | 
 |         } | 
 |         LocalFree(info); | 
 |     } | 
 |     if (psz && csz) | 
 |     { | 
 |         *(psz + ret) = '\0'; | 
 |         csz--; | 
 |         ret++; | 
 |     } | 
 |     else | 
 |         ret++; | 
 |     TRACE("Returning %s\n", debugstr_a(psz)); | 
 |     return ret; | 
 | } | 
 |  | 
 | /* Adds the prefix prefix to the wide-character string pointed to by psz, | 
 |  * followed by the character '='.  Copies no more than csz characters.  Returns | 
 |  * the number of characters copied.  If psz is NULL, returns the number of | 
 |  * characters that would be copied. | 
 |  * Assumes the characters in prefix are ASCII (not multibyte characters.) | 
 |  */ | 
 | static DWORD CRYPT_AddPrefixAToW(LPCSTR prefix, LPWSTR psz, DWORD csz) | 
 | { | 
 |     DWORD chars; | 
 |  | 
 |     TRACE("(%s, %p, %d)\n", debugstr_a(prefix), psz, csz); | 
 |  | 
 |     if (psz) | 
 |     { | 
 |         DWORD i; | 
 |  | 
 |         chars = min(lstrlenA(prefix), csz); | 
 |         for (i = 0; i < chars; i++) | 
 |             *(psz + i) = prefix[i]; | 
 |         csz -= chars; | 
 |         *(psz + chars) = '='; | 
 |         chars++; | 
 |         csz--; | 
 |     } | 
 |     else | 
 |         chars = lstrlenA(prefix) + 1; | 
 |     return chars; | 
 | } | 
 |  | 
 | /* Adds the prefix prefix to the string pointed to by psz, followed by the | 
 |  * character '='.  Copies no more than csz characters.  Returns the number of | 
 |  * characters copied.  If psz is NULL, returns the number of characters that | 
 |  * would be copied. | 
 |  */ | 
 | static DWORD CRYPT_AddPrefixW(LPCWSTR prefix, LPWSTR psz, DWORD csz) | 
 | { | 
 |     DWORD chars; | 
 |  | 
 |     TRACE("(%s, %p, %d)\n", debugstr_w(prefix), psz, csz); | 
 |  | 
 |     if (psz) | 
 |     { | 
 |         chars = min(lstrlenW(prefix), csz); | 
 |         memcpy(psz, prefix, chars * sizeof(WCHAR)); | 
 |         csz -= chars; | 
 |         *(psz + chars) = '='; | 
 |         chars++; | 
 |         csz--; | 
 |     } | 
 |     else | 
 |         chars = lstrlenW(prefix) + 1; | 
 |     return chars; | 
 | } | 
 |  | 
 | DWORD WINAPI CertNameToStrW(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName, | 
 |  DWORD dwStrType, LPWSTR psz, DWORD csz) | 
 | { | 
 |     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG | | 
 |      CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG; | 
 |     static const WCHAR commaSep[] = { ',',' ',0 }; | 
 |     static const WCHAR semiSep[] = { ';',' ',0 }; | 
 |     static const WCHAR crlfSep[] = { '\r','\n',0 }; | 
 |     static const WCHAR plusSep[] = { ' ','+',' ',0 }; | 
 |     static const WCHAR spaceSep[] = { ' ',0 }; | 
 |     DWORD ret = 0, bytes = 0; | 
 |     BOOL bRet; | 
 |     CERT_NAME_INFO *info; | 
 |  | 
 |     TRACE("(%d, %p, %08x, %p, %d)\n", dwCertEncodingType, pName, dwStrType, | 
 |      psz, csz); | 
 |     if (dwStrType & unsupportedFlags) | 
 |         FIXME("unsupported flags: %08x\n", dwStrType & unsupportedFlags); | 
 |  | 
 |     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData, | 
 |      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes); | 
 |     if (bRet) | 
 |     { | 
 |         DWORD i, j, sepLen, rdnSepLen; | 
 |         LPCWSTR sep, rdnSep; | 
 |  | 
 |         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG) | 
 |             sep = semiSep; | 
 |         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG) | 
 |             sep = crlfSep; | 
 |         else | 
 |             sep = commaSep; | 
 |         sepLen = lstrlenW(sep); | 
 |         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG) | 
 |             rdnSep = spaceSep; | 
 |         else | 
 |             rdnSep = plusSep; | 
 |         rdnSepLen = lstrlenW(rdnSep); | 
 |         for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++) | 
 |         { | 
 |             for (j = 0; (!psz || ret < csz) && j < info->rgRDN[i].cRDNAttr; j++) | 
 |             { | 
 |                 DWORD chars; | 
 |                 LPCSTR prefixA = NULL; | 
 |                 LPCWSTR prefixW = NULL; | 
 |  | 
 |                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR) | 
 |                     prefixA = info->rgRDN[i].rgRDNAttr[j].pszObjId; | 
 |                 else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR) | 
 |                 { | 
 |                     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo( | 
 |                      CRYPT_OID_INFO_OID_KEY, | 
 |                      info->rgRDN[i].rgRDNAttr[j].pszObjId, | 
 |                      CRYPT_RDN_ATTR_OID_GROUP_ID); | 
 |  | 
 |                     if (oidInfo) | 
 |                         prefixW = oidInfo->pwszName; | 
 |                     else | 
 |                         prefixA = info->rgRDN[i].rgRDNAttr[j].pszObjId; | 
 |                 } | 
 |                 if (prefixW) | 
 |                 { | 
 |                     /* - 1 is needed to account for the NULL terminator. */ | 
 |                     chars = CRYPT_AddPrefixW(prefixW, | 
 |                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0); | 
 |                     ret += chars; | 
 |                     csz -= chars; | 
 |                 } | 
 |                 else if (prefixA) | 
 |                 { | 
 |                     /* - 1 is needed to account for the NULL terminator. */ | 
 |                     chars = CRYPT_AddPrefixAToW(prefixA, | 
 |                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0); | 
 |                     ret += chars; | 
 |                     csz -= chars; | 
 |                 } | 
 |                 /* FIXME: handle quoting */ | 
 |                 chars = CertRDNValueToStrW( | 
 |                  info->rgRDN[i].rgRDNAttr[j].dwValueType,  | 
 |                  &info->rgRDN[i].rgRDNAttr[j].Value, psz ? psz + ret : NULL, | 
 |                  psz ? csz - ret : 0); | 
 |                 if (chars) | 
 |                     ret += chars - 1; | 
 |                 if (j < info->rgRDN[i].cRDNAttr - 1) | 
 |                 { | 
 |                     if (psz && ret < csz - rdnSepLen - 1) | 
 |                         memcpy(psz + ret, rdnSep, rdnSepLen * sizeof(WCHAR)); | 
 |                     ret += rdnSepLen; | 
 |                 } | 
 |             } | 
 |             if (i < info->cRDN - 1) | 
 |             { | 
 |                 if (psz && ret < csz - sepLen - 1) | 
 |                     memcpy(psz + ret, sep, sepLen * sizeof(WCHAR)); | 
 |                 ret += sepLen; | 
 |             } | 
 |         } | 
 |         LocalFree(info); | 
 |     } | 
 |     if (psz && csz) | 
 |     { | 
 |         *(psz + ret) = '\0'; | 
 |         csz--; | 
 |         ret++; | 
 |     } | 
 |     else | 
 |         ret++; | 
 |     TRACE("Returning %s\n", debugstr_w(psz)); | 
 |     return ret; | 
 | } | 
 |  | 
 | BOOL WINAPI CertStrToNameA(DWORD dwCertEncodingType, LPCSTR pszX500, | 
 |  DWORD dwStrType, void *pvReserved, BYTE *pbEncoded, DWORD *pcbEncoded, | 
 |  LPCSTR *ppszError) | 
 | { | 
 |     BOOL ret; | 
 |     int len; | 
 |  | 
 |     TRACE("(%08x, %s, %08x, %p, %p, %p, %p)\n", dwCertEncodingType, | 
 |      debugstr_a(pszX500), dwStrType, pvReserved, pbEncoded, pcbEncoded, | 
 |      ppszError); | 
 |  | 
 |     len = MultiByteToWideChar(CP_ACP, 0, pszX500, -1, NULL, 0); | 
 |     if (len) | 
 |     { | 
 |         LPWSTR x500, errorStr; | 
 |  | 
 |         if ((x500 = CryptMemAlloc(len * sizeof(WCHAR)))) | 
 |         { | 
 |             MultiByteToWideChar(CP_ACP, 0, pszX500, -1, x500, len); | 
 |             ret = CertStrToNameW(dwCertEncodingType, x500, dwStrType, | 
 |              pvReserved, pbEncoded, pcbEncoded, | 
 |              ppszError ? (LPCWSTR *)&errorStr : NULL); | 
 |             if (ppszError) | 
 |             { | 
 |                 if (!ret) | 
 |                 { | 
 |                     DWORD i; | 
 |  | 
 |                     *ppszError = pszX500; | 
 |                     for (i = 0; i < errorStr - x500; i++) | 
 |                         *ppszError = CharNextA(*ppszError); | 
 |                 } | 
 |                 else | 
 |                     *ppszError = NULL; | 
 |             } | 
 |             CryptMemFree(x500); | 
 |         } | 
 |         else | 
 |         { | 
 |             SetLastError(ERROR_OUTOFMEMORY); | 
 |             ret = FALSE; | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         SetLastError(CRYPT_E_INVALID_X500_STRING); | 
 |         if (ppszError) | 
 |             *ppszError = pszX500; | 
 |         ret = FALSE; | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | struct KeynameKeeper | 
 | { | 
 |     WCHAR  buf[10]; /* big enough for L"GivenName" */ | 
 |     LPWSTR keyName; /* usually = buf, but may be allocated */ | 
 |     DWORD  keyLen; | 
 | }; | 
 |  | 
 | static void CRYPT_InitializeKeynameKeeper(struct KeynameKeeper *keeper) | 
 | { | 
 |     keeper->keyName = keeper->buf; | 
 |     keeper->keyLen = sizeof(keeper->buf) / sizeof(keeper->buf[0]); | 
 | } | 
 |  | 
 | static void CRYPT_FreeKeynameKeeper(struct KeynameKeeper *keeper) | 
 | { | 
 |     if (keeper->keyName != keeper->buf) | 
 |         CryptMemFree(keeper->keyName); | 
 | } | 
 |  | 
 | struct X500TokenW | 
 | { | 
 |     LPCWSTR start; | 
 |     LPCWSTR end; | 
 | }; | 
 |  | 
 | static void CRYPT_KeynameKeeperFromTokenW(struct KeynameKeeper *keeper, | 
 |  const struct X500TokenW *key) | 
 | { | 
 |     DWORD len = key->end - key->start; | 
 |  | 
 |     if (len > keeper->keyLen) | 
 |     { | 
 |         if (keeper->keyName == keeper->buf) | 
 |             keeper->keyName = CryptMemAlloc(len * sizeof(WCHAR)); | 
 |         else | 
 |             keeper->keyName = CryptMemRealloc(keeper->keyName, | 
 |              len * sizeof(WCHAR)); | 
 |         keeper->keyLen = len; | 
 |     } | 
 |     memcpy(keeper->keyName, key->start, (key->end - key->start) * | 
 |      sizeof(WCHAR)); | 
 |     keeper->keyName[len] = '\0'; | 
 |     TRACE("Keyname is %s\n", debugstr_w(keeper->keyName)); | 
 | } | 
 |  | 
 | static BOOL CRYPT_GetNextKeyW(LPCWSTR str, struct X500TokenW *token, | 
 |  LPCWSTR *ppszError) | 
 | { | 
 |     BOOL ret = TRUE; | 
 |  | 
 |     while (*str && isspaceW(*str)) | 
 |         str++; | 
 |     if (*str) | 
 |     { | 
 |         token->start = str; | 
 |         while (*str && *str != '=' && !isspaceW(*str)) | 
 |             str++; | 
 |         if (*str && (*str == '=' || isspaceW(*str))) | 
 |             token->end = str; | 
 |         else | 
 |         { | 
 |             TRACE("missing equals char at %s\n", debugstr_w(token->start)); | 
 |             if (ppszError) | 
 |                 *ppszError = token->start; | 
 |             SetLastError(CRYPT_E_INVALID_X500_STRING); | 
 |             ret = FALSE; | 
 |         } | 
 |     } | 
 |     else | 
 |         token->start = NULL; | 
 |     return ret; | 
 | } | 
 |  | 
 | /* Assumes separators are characters in the 0-255 range */ | 
 | static BOOL CRYPT_GetNextValueW(LPCWSTR str, DWORD dwFlags, LPCWSTR separators, | 
 |  struct X500TokenW *token, LPCWSTR *ppszError) | 
 | { | 
 |     BOOL ret = TRUE; | 
 |  | 
 |     TRACE("(%s, %s, %p, %p)\n", debugstr_w(str), debugstr_w(separators), token, | 
 |      ppszError); | 
 |  | 
 |     while (*str && isspaceW(*str)) | 
 |         str++; | 
 |     if (*str) | 
 |     { | 
 |         token->start = str; | 
 |         if (!(dwFlags & CERT_NAME_STR_NO_QUOTING_FLAG) && *str == '"') | 
 |         { | 
 |             token->end = NULL; | 
 |             str++; | 
 |             while (!token->end && ret) | 
 |             { | 
 |                 while (*str && *str != '"') | 
 |                     str++; | 
 |                 if (*str == '"') | 
 |                 { | 
 |                     if (*(str + 1) != '"') | 
 |                         token->end = str + 1; | 
 |                     else | 
 |                         str += 2; | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     TRACE("unterminated quote at %s\n", debugstr_w(str)); | 
 |                     if (ppszError) | 
 |                         *ppszError = str; | 
 |                     SetLastError(CRYPT_E_INVALID_X500_STRING); | 
 |                     ret = FALSE; | 
 |                 } | 
 |             } | 
 |         } | 
 |         else | 
 |         { | 
 |             WCHAR map[256] = { 0 }; | 
 |  | 
 |             while (*separators) | 
 |                 map[*separators++] = 1; | 
 |             while (*str && (*str >= 0xff || !map[*str])) | 
 |                 str++; | 
 |             token->end = str; | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         TRACE("missing value at %s\n", debugstr_w(str)); | 
 |         if (ppszError) | 
 |             *ppszError = str; | 
 |         SetLastError(CRYPT_E_INVALID_X500_STRING); | 
 |         ret = FALSE; | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | /* Encodes the string represented by value as the string type type into the | 
 |  * CERT_NAME_BLOB output.  If there is an error and ppszError is not NULL, | 
 |  * *ppszError is set to the first failing character.  If there is no error, | 
 |  * output's pbData must be freed with LocalFree. | 
 |  */ | 
 | static BOOL CRYPT_EncodeValueWithType(DWORD dwCertEncodingType, | 
 |  const struct X500TokenW *value, PCERT_NAME_BLOB output, DWORD type, | 
 |  LPCWSTR *ppszError) | 
 | { | 
 |     CERT_NAME_VALUE nameValue = { type, { 0, NULL } }; | 
 |     BOOL ret = TRUE; | 
 |  | 
 |     if (value->end > value->start) | 
 |     { | 
 |         nameValue.Value.pbData = CryptMemAlloc((value->end - value->start) * | 
 |          sizeof(WCHAR)); | 
 |         if (!nameValue.Value.pbData) | 
 |         { | 
 |             SetLastError(ERROR_OUTOFMEMORY); | 
 |             ret = FALSE; | 
 |         } | 
 |     } | 
 |     if (ret) | 
 |     { | 
 |         if (value->end > value->start) | 
 |         { | 
 |             DWORD i; | 
 |             LPWSTR ptr = (LPWSTR)nameValue.Value.pbData; | 
 |  | 
 |             for (i = 0; i < value->end - value->start; i++) | 
 |             { | 
 |                 *ptr++ = value->start[i]; | 
 |                 if (value->start[i] == '"') | 
 |                     i++; | 
 |             } | 
 |             nameValue.Value.cbData = (LPBYTE)ptr - nameValue.Value.pbData; | 
 |         } | 
 |         ret = CryptEncodeObjectEx(dwCertEncodingType, X509_UNICODE_NAME_VALUE, | 
 |          &nameValue, CRYPT_ENCODE_ALLOC_FLAG, NULL, &output->pbData, | 
 |          &output->cbData); | 
 |         if (!ret && ppszError) | 
 |         { | 
 |             if (type == CERT_RDN_NUMERIC_STRING && | 
 |              GetLastError() == CRYPT_E_INVALID_NUMERIC_STRING) | 
 |                 *ppszError = value->start + output->cbData; | 
 |             else if (type == CERT_RDN_PRINTABLE_STRING && | 
 |              GetLastError() == CRYPT_E_INVALID_PRINTABLE_STRING) | 
 |                 *ppszError = value->start + output->cbData; | 
 |             else if (type == CERT_RDN_IA5_STRING && | 
 |              GetLastError() == CRYPT_E_INVALID_IA5_STRING) | 
 |                 *ppszError = value->start + output->cbData; | 
 |         } | 
 |         CryptMemFree(nameValue.Value.pbData); | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | static BOOL CRYPT_EncodeValue(DWORD dwCertEncodingType, | 
 |  const struct X500TokenW *value, PCERT_NAME_BLOB output, const DWORD *types, | 
 |  LPCWSTR *ppszError) | 
 | { | 
 |     DWORD i; | 
 |     BOOL ret; | 
 |  | 
 |     ret = FALSE; | 
 |     for (i = 0; !ret && types[i]; i++) | 
 |         ret = CRYPT_EncodeValueWithType(dwCertEncodingType, value, output, | 
 |          types[i], ppszError); | 
 |     return ret; | 
 | } | 
 |  | 
 | static BOOL CRYPT_ValueToRDN(DWORD dwCertEncodingType, PCERT_NAME_INFO info, | 
 |  PCCRYPT_OID_INFO keyOID, struct X500TokenW *value, LPCWSTR *ppszError) | 
 | { | 
 |     BOOL ret = FALSE; | 
 |  | 
 |     TRACE("OID %s, value %s\n", debugstr_a(keyOID->pszOID), | 
 |      debugstr_wn(value->start, value->end - value->start)); | 
 |  | 
 |     if (!info->rgRDN) | 
 |         info->rgRDN = CryptMemAlloc(sizeof(CERT_RDN)); | 
 |     else | 
 |         info->rgRDN = CryptMemRealloc(info->rgRDN, | 
 |          (info->cRDN + 1) * sizeof(CERT_RDN)); | 
 |     if (info->rgRDN) | 
 |     { | 
 |         /* FIXME: support multiple RDN attrs */ | 
 |         info->rgRDN[info->cRDN].rgRDNAttr = | 
 |          CryptMemAlloc(sizeof(CERT_RDN_ATTR)); | 
 |         if (info->rgRDN[info->cRDN].rgRDNAttr) | 
 |         { | 
 |             static const DWORD defaultTypes[] = { CERT_RDN_PRINTABLE_STRING, | 
 |              CERT_RDN_BMP_STRING, 0 }; | 
 |             const DWORD *types; | 
 |  | 
 |             info->rgRDN[info->cRDN].cRDNAttr = 1; | 
 |             info->rgRDN[info->cRDN].rgRDNAttr[0].pszObjId = | 
 |              (LPSTR)keyOID->pszOID; | 
 |             info->rgRDN[info->cRDN].rgRDNAttr[0].dwValueType = | 
 |              CERT_RDN_ENCODED_BLOB; | 
 |             if (keyOID->ExtraInfo.cbData) | 
 |                 types = (const DWORD *)keyOID->ExtraInfo.pbData; | 
 |             else | 
 |                 types = defaultTypes; | 
 |  | 
 |             /* Remove surrounding quotes */ | 
 |             if (value->start[0] == '"') | 
 |             { | 
 |                 value->start++; | 
 |                 value->end--; | 
 |             } | 
 |             ret = CRYPT_EncodeValue(dwCertEncodingType, value, | 
 |              &info->rgRDN[info->cRDN].rgRDNAttr[0].Value, types, ppszError); | 
 |         } | 
 |         else | 
 |             SetLastError(ERROR_OUTOFMEMORY); | 
 |         info->cRDN++; | 
 |     } | 
 |     else | 
 |         SetLastError(ERROR_OUTOFMEMORY); | 
 |     return ret; | 
 | } | 
 |  | 
 | BOOL WINAPI CertStrToNameW(DWORD dwCertEncodingType, LPCWSTR pszX500, | 
 |  DWORD dwStrType, void *pvReserved, BYTE *pbEncoded, DWORD *pcbEncoded, | 
 |  LPCWSTR *ppszError) | 
 | { | 
 |     CERT_NAME_INFO info = { 0, NULL }; | 
 |     LPCWSTR str; | 
 |     struct KeynameKeeper keeper; | 
 |     DWORD i; | 
 |     BOOL ret = TRUE; | 
 |  | 
 |     TRACE("(%08x, %s, %08x, %p, %p, %p, %p)\n", dwCertEncodingType, | 
 |      debugstr_w(pszX500), dwStrType, pvReserved, pbEncoded, pcbEncoded, | 
 |      ppszError); | 
 |  | 
 |     CRYPT_InitializeKeynameKeeper(&keeper); | 
 |     str = pszX500; | 
 |     while (str && *str && ret) | 
 |     { | 
 |         struct X500TokenW token; | 
 |  | 
 |         ret = CRYPT_GetNextKeyW(str, &token, ppszError); | 
 |         if (ret && token.start) | 
 |         { | 
 |             PCCRYPT_OID_INFO keyOID; | 
 |  | 
 |             CRYPT_KeynameKeeperFromTokenW(&keeper, &token); | 
 |             keyOID = CryptFindOIDInfo(CRYPT_OID_INFO_NAME_KEY, keeper.keyName, | 
 |              CRYPT_RDN_ATTR_OID_GROUP_ID); | 
 |             if (!keyOID) | 
 |             { | 
 |                 if (ppszError) | 
 |                     *ppszError = token.start; | 
 |                 SetLastError(CRYPT_E_INVALID_X500_STRING); | 
 |                 ret = FALSE; | 
 |             } | 
 |             else | 
 |             { | 
 |                 str = token.end; | 
 |                 while (isspace(*str)) | 
 |                     str++; | 
 |                 if (*str != '=') | 
 |                 { | 
 |                     if (ppszError) | 
 |                         *ppszError = str; | 
 |                     SetLastError(CRYPT_E_INVALID_X500_STRING); | 
 |                     ret = FALSE; | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     static const WCHAR commaSep[] = { ',',0 }; | 
 |                     static const WCHAR semiSep[] = { ';',0 }; | 
 |                     static const WCHAR crlfSep[] = { '\r','\n',0 }; | 
 |                     static const WCHAR allSeps[] = { ',',';','\r','\n',0 }; | 
 |                     LPCWSTR sep; | 
 |  | 
 |                     str++; | 
 |                     if (dwStrType & CERT_NAME_STR_COMMA_FLAG) | 
 |                         sep = commaSep; | 
 |                     else if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG) | 
 |                         sep = semiSep; | 
 |                     else if (dwStrType & CERT_NAME_STR_CRLF_FLAG) | 
 |                         sep = crlfSep; | 
 |                     else | 
 |                         sep = allSeps; | 
 |                     ret = CRYPT_GetNextValueW(str, dwStrType, sep, &token, | 
 |                      ppszError); | 
 |                     if (ret) | 
 |                     { | 
 |                         str = token.end; | 
 |                         ret = CRYPT_ValueToRDN(dwCertEncodingType, &info, | 
 |                          keyOID, &token, ppszError); | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 |     CRYPT_FreeKeynameKeeper(&keeper); | 
 |     if (ret) | 
 |     { | 
 |         if (ppszError) | 
 |             *ppszError = NULL; | 
 |         ret = CryptEncodeObjectEx(dwCertEncodingType, X509_NAME, &info, | 
 |          0, NULL, pbEncoded, pcbEncoded); | 
 |     } | 
 |     for (i = 0; i < info.cRDN; i++) | 
 |     { | 
 |         DWORD j; | 
 |  | 
 |         for (j = 0; j < info.rgRDN[i].cRDNAttr; j++) | 
 |             LocalFree(info.rgRDN[i].rgRDNAttr[j].Value.pbData); | 
 |         CryptMemFree(info.rgRDN[i].rgRDNAttr); | 
 |     } | 
 |     CryptMemFree(info.rgRDN); | 
 |     return ret; | 
 | } | 
 |  | 
 | DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType, | 
 |  DWORD dwFlags, void *pvTypePara, LPSTR pszNameString, DWORD cchNameString) | 
 | { | 
 |     DWORD ret; | 
 |  | 
 |     TRACE("(%p, %d, %08x, %p, %p, %d)\n", pCertContext, dwType, dwFlags, | 
 |      pvTypePara, pszNameString, cchNameString); | 
 |  | 
 |     if (pszNameString) | 
 |     { | 
 |         LPWSTR wideName; | 
 |         DWORD nameLen; | 
 |  | 
 |         nameLen = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara, | 
 |          NULL, 0); | 
 |         wideName = CryptMemAlloc(nameLen * sizeof(WCHAR)); | 
 |         if (wideName) | 
 |         { | 
 |             CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara, | 
 |              wideName, nameLen); | 
 |             nameLen = WideCharToMultiByte(CP_ACP, 0, wideName, nameLen, | 
 |              pszNameString, cchNameString, NULL, NULL); | 
 |             if (nameLen <= cchNameString) | 
 |                 ret = nameLen; | 
 |             else | 
 |             { | 
 |                 pszNameString[cchNameString - 1] = '\0'; | 
 |                 ret = cchNameString; | 
 |             } | 
 |             CryptMemFree(wideName); | 
 |         } | 
 |         else | 
 |         { | 
 |             *pszNameString = '\0'; | 
 |             ret = 1; | 
 |         } | 
 |     } | 
 |     else | 
 |         ret = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara, | 
 |          NULL, 0); | 
 |     return ret; | 
 | } | 
 |  | 
 | DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType, | 
 |  DWORD dwFlags, void *pvTypePara, LPWSTR pszNameString, DWORD cchNameString) | 
 | { | 
 |     DWORD ret; | 
 |     PCERT_NAME_BLOB name; | 
 |     LPCSTR altNameOID; | 
 |  | 
 |     TRACE("(%p, %d, %08x, %p, %p, %d)\n", pCertContext, dwType, | 
 |      dwFlags, pvTypePara, pszNameString, cchNameString); | 
 |  | 
 |     if (dwFlags & CERT_NAME_ISSUER_FLAG) | 
 |     { | 
 |         name = &pCertContext->pCertInfo->Issuer; | 
 |         altNameOID = szOID_ISSUER_ALT_NAME; | 
 |     } | 
 |     else | 
 |     { | 
 |         name = &pCertContext->pCertInfo->Subject; | 
 |         altNameOID = szOID_SUBJECT_ALT_NAME; | 
 |     } | 
 |  | 
 |     switch (dwType) | 
 |     { | 
 |     case CERT_NAME_SIMPLE_DISPLAY_TYPE: | 
 |     { | 
 |         static const LPCSTR simpleAttributeOIDs[] = { szOID_COMMON_NAME, | 
 |          szOID_ORGANIZATIONAL_UNIT_NAME, szOID_ORGANIZATION_NAME, | 
 |          szOID_RSA_emailAddr }; | 
 |         CERT_NAME_INFO *info = NULL; | 
 |         PCERT_RDN_ATTR nameAttr = NULL; | 
 |         DWORD bytes = 0, i; | 
 |  | 
 |         if (CryptDecodeObjectEx(pCertContext->dwCertEncodingType, X509_NAME, | 
 |          name->pbData, name->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, | 
 |          &bytes)) | 
 |         { | 
 |             for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) / | 
 |              sizeof(simpleAttributeOIDs[0]); i++) | 
 |                 nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], info); | 
 |         } | 
 |         if (!nameAttr) | 
 |         { | 
 |             PCERT_EXTENSION ext = CertFindExtension(altNameOID, | 
 |              pCertContext->pCertInfo->cExtension, | 
 |              pCertContext->pCertInfo->rgExtension); | 
 |  | 
 |             if (ext) | 
 |             { | 
 |                 for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) / | 
 |                  sizeof(simpleAttributeOIDs[0]); i++) | 
 |                     nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], info); | 
 |                 if (!nameAttr) | 
 |                 { | 
 |                     /* FIXME: gotta then look for a rfc822Name choice in ext. | 
 |                      * Failing that, look for the first attribute. | 
 |                      */ | 
 |                     FIXME("CERT_NAME_SIMPLE_DISPLAY_TYPE: stub\n"); | 
 |                 } | 
 |             } | 
 |         } | 
 |         if (nameAttr) | 
 |             ret = CertRDNValueToStrW(nameAttr->dwValueType, &nameAttr->Value, | 
 |                                      pszNameString, cchNameString); | 
 |         else | 
 |             ret = 0; | 
 |         if (info) | 
 |             LocalFree(info); | 
 |         break; | 
 |     } | 
 |     case CERT_NAME_FRIENDLY_DISPLAY_TYPE: | 
 |     { | 
 |         DWORD cch = cchNameString; | 
 |  | 
 |         if (CertGetCertificateContextProperty(pCertContext, | 
 |          CERT_FRIENDLY_NAME_PROP_ID, pszNameString, &cch)) | 
 |             ret = cch; | 
 |         else | 
 |             ret = CertGetNameStringW(pCertContext, | 
 |              CERT_NAME_SIMPLE_DISPLAY_TYPE, dwFlags, pvTypePara, pszNameString, | 
 |              cchNameString); | 
 |         break; | 
 |     } | 
 |     default: | 
 |         FIXME("unimplemented for type %d\n", dwType); | 
 |         ret = 0; | 
 |     } | 
 |     return ret; | 
 | } |