| /* | |
| * 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> | |
| #define NONAMELESSUNION | |
| #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(strlen(prefix), csz); | |
| memcpy(psz, prefix, chars); | |
| *(psz + chars) = '='; | |
| chars++; | |
| } | |
| 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_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; | |
| BOOL reverse = dwStrType & CERT_NAME_STR_REVERSE_FLAG; | |
| const CERT_RDN *rdn = info->rgRDN; | |
| if(reverse && info->cRDN > 1) rdn += (info->cRDN - 1); | |
| 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 < rdn->cRDNAttr; j++) | |
| { | |
| DWORD chars; | |
| char prefixBuf[10]; /* big enough for GivenName */ | |
| LPCSTR prefix = NULL; | |
| if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR) | |
| prefix = rdn->rgRDNAttr[j].pszObjId; | |
| else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR) | |
| { | |
| PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo( | |
| CRYPT_OID_INFO_OID_KEY, | |
| rdn->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 = rdn->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; | |
| } | |
| /* FIXME: handle quoting */ | |
| chars = CertRDNValueToStrA( | |
| rdn->rgRDNAttr[j].dwValueType, | |
| &rdn->rgRDNAttr[j].Value, psz ? psz + ret : NULL, | |
| psz ? csz - ret : 0); | |
| if (chars) | |
| ret += chars - 1; | |
| if (j < rdn->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; | |
| } | |
| if(reverse) rdn--; | |
| else rdn++; | |
| } | |
| LocalFree(info); | |
| } | |
| if (psz && csz) | |
| { | |
| *(psz + ret) = '\0'; | |
| 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(strlen(prefix), csz); | |
| for (i = 0; i < chars; i++) | |
| *(psz + i) = prefix[i]; | |
| *(psz + chars) = '='; | |
| chars++; | |
| } | |
| 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(strlenW(prefix), csz); | |
| memcpy(psz, prefix, chars * sizeof(WCHAR)); | |
| *(psz + chars) = '='; | |
| chars++; | |
| } | |
| else | |
| chars = lstrlenW(prefix) + 1; | |
| return chars; | |
| } | |
| static const WCHAR indent[] = { ' ',' ',' ',' ',' ',0 }; | |
| DWORD cert_name_to_str_with_indent(DWORD dwCertEncodingType, DWORD indentLevel, | |
| const CERT_NAME_BLOB *pName, DWORD dwStrType, LPWSTR psz, DWORD csz) | |
| { | |
| static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_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; | |
| 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; | |
| BOOL reverse = dwStrType & CERT_NAME_STR_REVERSE_FLAG; | |
| const CERT_RDN *rdn = info->rgRDN; | |
| if(reverse && info->cRDN > 1) rdn += (info->cRDN - 1); | |
| 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 < rdn->cRDNAttr; j++) | |
| { | |
| DWORD chars; | |
| LPCSTR prefixA = NULL; | |
| LPCWSTR prefixW = NULL; | |
| if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR) | |
| prefixA = rdn->rgRDNAttr[j].pszObjId; | |
| else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR) | |
| { | |
| PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo( | |
| CRYPT_OID_INFO_OID_KEY, | |
| rdn->rgRDNAttr[j].pszObjId, | |
| CRYPT_RDN_ATTR_OID_GROUP_ID); | |
| if (oidInfo) | |
| prefixW = oidInfo->pwszName; | |
| else | |
| prefixA = rdn->rgRDNAttr[j].pszObjId; | |
| } | |
| if (dwStrType & CERT_NAME_STR_CRLF_FLAG) | |
| { | |
| DWORD k; | |
| for (k = 0; k < indentLevel; k++) | |
| { | |
| if (psz) | |
| { | |
| chars = min(strlenW(indent), csz - ret - 1); | |
| memcpy(psz + ret, indent, chars * sizeof(WCHAR)); | |
| } | |
| else | |
| chars = strlenW(indent); | |
| ret += chars; | |
| } | |
| } | |
| 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; | |
| } | |
| 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; | |
| } | |
| /* FIXME: handle quoting */ | |
| chars = CertRDNValueToStrW( | |
| rdn->rgRDNAttr[j].dwValueType, | |
| &rdn->rgRDNAttr[j].Value, psz ? psz + ret : NULL, | |
| psz ? csz - ret : 0); | |
| if (chars) | |
| ret += chars - 1; | |
| if (j < rdn->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; | |
| } | |
| if(reverse) rdn--; | |
| else rdn++; | |
| } | |
| LocalFree(info); | |
| } | |
| if (psz && csz) | |
| { | |
| *(psz + ret) = '\0'; | |
| ret++; | |
| } | |
| else | |
| ret++; | |
| return ret; | |
| } | |
| DWORD WINAPI CertNameToStrW(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName, | |
| DWORD dwStrType, LPWSTR psz, DWORD csz) | |
| { | |
| BOOL ret; | |
| TRACE("(%d, %p, %08x, %p, %d)\n", dwCertEncodingType, pName, dwStrType, | |
| psz, csz); | |
| ret = cert_name_to_str_with_indent(dwCertEncodingType, 0, pName, dwStrType, | |
| psz, csz); | |
| 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) | |
| { | |
| LONG 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) | |
| { | |
| LONG 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; | |
| } | |
| /* Searches cert's extensions for the alternate name extension with OID | |
| * altNameOID, and if found, searches it for the alternate name type entryType. | |
| * If found, returns a pointer to the entry, otherwise returns NULL. | |
| * Regardless of whether an entry of the desired type is found, if the | |
| * alternate name extension is present, sets *info to the decoded alternate | |
| * name extension, which you must free using LocalFree. | |
| * The return value is a pointer within *info, so don't free *info before | |
| * you're done with the return value. | |
| */ | |
| static PCERT_ALT_NAME_ENTRY cert_find_alt_name_entry(PCCERT_CONTEXT cert, | |
| LPCSTR altNameOID, DWORD entryType, PCERT_ALT_NAME_INFO *info) | |
| { | |
| PCERT_ALT_NAME_ENTRY entry = NULL; | |
| PCERT_EXTENSION ext = CertFindExtension(altNameOID, | |
| cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension); | |
| if (ext) | |
| { | |
| DWORD bytes = 0; | |
| if (CryptDecodeObjectEx(cert->dwCertEncodingType, X509_ALTERNATE_NAME, | |
| ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, | |
| info, &bytes)) | |
| { | |
| DWORD i; | |
| for (i = 0; !entry && i < (*info)->cAltEntry; i++) | |
| if ((*info)->rgAltEntry[i].dwAltNameChoice == entryType) | |
| entry = &(*info)->rgAltEntry[i]; | |
| } | |
| } | |
| else | |
| *info = NULL; | |
| return entry; | |
| } | |
| static DWORD cert_get_name_from_rdn_attr(DWORD encodingType, | |
| PCERT_NAME_BLOB name, LPCSTR oid, LPWSTR pszNameString, DWORD cchNameString) | |
| { | |
| CERT_NAME_INFO *nameInfo; | |
| DWORD bytes = 0, ret = 0; | |
| if (CryptDecodeObjectEx(encodingType, X509_NAME, name->pbData, | |
| name->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &nameInfo, &bytes)) | |
| { | |
| PCERT_RDN_ATTR nameAttr = CertFindRDNAttr(oid, nameInfo); | |
| if (nameAttr) | |
| ret = CertRDNValueToStrW(nameAttr->dwValueType, &nameAttr->Value, | |
| pszNameString, cchNameString); | |
| LocalFree(nameInfo); | |
| } | |
| return ret; | |
| } | |
| DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType, | |
| DWORD dwFlags, void *pvTypePara, LPWSTR pszNameString, DWORD cchNameString) | |
| { | |
| DWORD ret = 0; | |
| 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_EMAIL_TYPE: | |
| { | |
| CERT_ALT_NAME_INFO *info; | |
| PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext, | |
| altNameOID, CERT_ALT_NAME_RFC822_NAME, &info); | |
| if (entry) | |
| { | |
| if (!pszNameString) | |
| ret = strlenW(entry->u.pwszRfc822Name) + 1; | |
| else if (cchNameString) | |
| { | |
| ret = min(strlenW(entry->u.pwszRfc822Name), cchNameString - 1); | |
| memcpy(pszNameString, entry->u.pwszRfc822Name, | |
| ret * sizeof(WCHAR)); | |
| pszNameString[ret++] = 0; | |
| } | |
| } | |
| if (info) | |
| LocalFree(info); | |
| if (!ret) | |
| ret = cert_get_name_from_rdn_attr(pCertContext->dwCertEncodingType, | |
| name, szOID_RSA_emailAddr, pszNameString, cchNameString); | |
| break; | |
| } | |
| case CERT_NAME_RDN_TYPE: | |
| if (name->cbData) | |
| ret = CertNameToStrW(pCertContext->dwCertEncodingType, name, | |
| *(DWORD *)pvTypePara, pszNameString, cchNameString); | |
| else | |
| { | |
| CERT_ALT_NAME_INFO *info; | |
| PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext, | |
| altNameOID, CERT_ALT_NAME_DIRECTORY_NAME, &info); | |
| if (entry) | |
| ret = CertNameToStrW(pCertContext->dwCertEncodingType, | |
| &entry->u.DirectoryName, *(DWORD *)pvTypePara, pszNameString, | |
| cchNameString); | |
| if (info) | |
| LocalFree(info); | |
| } | |
| break; | |
| case CERT_NAME_ATTR_TYPE: | |
| ret = cert_get_name_from_rdn_attr(pCertContext->dwCertEncodingType, | |
| name, pvTypePara, pszNameString, cchNameString); | |
| if (!ret) | |
| { | |
| CERT_ALT_NAME_INFO *altInfo; | |
| PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext, | |
| altNameOID, CERT_ALT_NAME_DIRECTORY_NAME, &altInfo); | |
| if (entry) | |
| ret = cert_name_to_str_with_indent(X509_ASN_ENCODING, 0, | |
| &entry->u.DirectoryName, 0, pszNameString, cchNameString); | |
| if (altInfo) | |
| LocalFree(altInfo); | |
| } | |
| break; | |
| 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 *nameInfo = NULL; | |
| DWORD bytes = 0, i; | |
| if (CryptDecodeObjectEx(pCertContext->dwCertEncodingType, X509_NAME, | |
| name->pbData, name->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &nameInfo, | |
| &bytes)) | |
| { | |
| PCERT_RDN_ATTR nameAttr = NULL; | |
| for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) / | |
| sizeof(simpleAttributeOIDs[0]); i++) | |
| nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], nameInfo); | |
| if (nameAttr) | |
| ret = CertRDNValueToStrW(nameAttr->dwValueType, | |
| &nameAttr->Value, pszNameString, cchNameString); | |
| LocalFree(nameInfo); | |
| } | |
| if (!ret) | |
| { | |
| CERT_ALT_NAME_INFO *altInfo; | |
| PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext, | |
| altNameOID, CERT_ALT_NAME_RFC822_NAME, &altInfo); | |
| if (altInfo) | |
| { | |
| if (!entry && altInfo->cAltEntry) | |
| entry = &altInfo->rgAltEntry[0]; | |
| if (entry) | |
| { | |
| if (!pszNameString) | |
| ret = strlenW(entry->u.pwszRfc822Name) + 1; | |
| else if (cchNameString) | |
| { | |
| ret = min(strlenW(entry->u.pwszRfc822Name), | |
| cchNameString - 1); | |
| memcpy(pszNameString, entry->u.pwszRfc822Name, | |
| ret * sizeof(WCHAR)); | |
| pszNameString[ret++] = 0; | |
| } | |
| } | |
| LocalFree(altInfo); | |
| } | |
| } | |
| 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; | |
| } | |
| case CERT_NAME_DNS_TYPE: | |
| { | |
| CERT_ALT_NAME_INFO *info; | |
| PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext, | |
| altNameOID, CERT_ALT_NAME_DNS_NAME, &info); | |
| if (entry) | |
| { | |
| if (!pszNameString) | |
| ret = strlenW(entry->u.pwszDNSName) + 1; | |
| else if (cchNameString) | |
| { | |
| ret = min(strlenW(entry->u.pwszDNSName), cchNameString - 1); | |
| memcpy(pszNameString, entry->u.pwszDNSName, ret * sizeof(WCHAR)); | |
| pszNameString[ret++] = 0; | |
| } | |
| } | |
| if (info) | |
| LocalFree(info); | |
| if (!ret) | |
| ret = cert_get_name_from_rdn_attr(pCertContext->dwCertEncodingType, | |
| name, szOID_COMMON_NAME, pszNameString, cchNameString); | |
| break; | |
| } | |
| case CERT_NAME_URL_TYPE: | |
| { | |
| CERT_ALT_NAME_INFO *info; | |
| PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext, | |
| altNameOID, CERT_ALT_NAME_URL, &info); | |
| if (entry) | |
| { | |
| if (!pszNameString) | |
| ret = strlenW(entry->u.pwszURL) + 1; | |
| else if (cchNameString) | |
| { | |
| ret = min(strlenW(entry->u.pwszURL), cchNameString - 1); | |
| memcpy(pszNameString, entry->u.pwszURL, ret * sizeof(WCHAR)); | |
| pszNameString[ret++] = 0; | |
| } | |
| } | |
| if (info) | |
| LocalFree(info); | |
| break; | |
| } | |
| default: | |
| FIXME("unimplemented for type %d\n", dwType); | |
| ret = 0; | |
| } | |
| if (!ret) | |
| { | |
| if (!pszNameString) | |
| ret = 1; | |
| else if (cchNameString) | |
| { | |
| pszNameString[0] = 0; | |
| ret = 1; | |
| } | |
| } | |
| return ret; | |
| } |