| /* |
| * 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, |
| const CERT_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; |
| } |