| /* | |
| * Copyright 2008 Juan Lang | |
| * | |
| * 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 "config.h" | |
| #include <stdarg.h> | |
| #define COBJMACROS | |
| #define NONAMELESSUNION | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winnls.h" | |
| #include "winuser.h" | |
| #include "softpub.h" | |
| #include "wingdi.h" | |
| #include "richedit.h" | |
| #include "ole2.h" | |
| #include "richole.h" | |
| #include "commdlg.h" | |
| #include "commctrl.h" | |
| #include "cryptuiapi.h" | |
| #include "cryptuires.h" | |
| #include "urlmon.h" | |
| #include "hlink.h" | |
| #include "winreg.h" | |
| #include "wine/debug.h" | |
| #include "wine/unicode.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(cryptui); | |
| static HINSTANCE hInstance; | |
| BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) | |
| { | |
| TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved); | |
| switch (fdwReason) | |
| { | |
| case DLL_WINE_PREATTACH: | |
| return FALSE; /* prefer native version */ | |
| case DLL_PROCESS_ATTACH: | |
| hInstance = hinstDLL; | |
| DisableThreadLibraryCalls(hinstDLL); | |
| break; | |
| case DLL_PROCESS_DETACH: | |
| break; | |
| default: | |
| break; | |
| } | |
| return TRUE; | |
| } | |
| #define MAX_STRING_LEN 512 | |
| static void add_cert_columns(HWND hwnd) | |
| { | |
| HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS); | |
| RECT rc; | |
| WCHAR buf[MAX_STRING_LEN]; | |
| LVCOLUMNW column; | |
| SendMessageW(lv, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT); | |
| GetWindowRect(lv, &rc); | |
| LoadStringW(hInstance, IDS_SUBJECT_COLUMN, buf, | |
| sizeof(buf) / sizeof(buf[0])); | |
| column.mask = LVCF_WIDTH | LVCF_TEXT; | |
| column.cx = (rc.right - rc.left) * 29 / 100 - 2; | |
| column.pszText = buf; | |
| SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column); | |
| LoadStringW(hInstance, IDS_ISSUER_COLUMN, buf, | |
| sizeof(buf) / sizeof(buf[0])); | |
| SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column); | |
| column.cx = (rc.right - rc.left) * 16 / 100 - 2; | |
| LoadStringW(hInstance, IDS_EXPIRATION_COLUMN, buf, | |
| sizeof(buf) / sizeof(buf[0])); | |
| SendMessageW(lv, LVM_INSERTCOLUMNW, 2, (LPARAM)&column); | |
| column.cx = (rc.right - rc.left) * 23 / 100 - 1; | |
| LoadStringW(hInstance, IDS_FRIENDLY_NAME_COLUMN, buf, | |
| sizeof(buf) / sizeof(buf[0])); | |
| SendMessageW(lv, LVM_INSERTCOLUMNW, 3, (LPARAM)&column); | |
| } | |
| static void add_cert_to_view(HWND lv, PCCERT_CONTEXT cert, DWORD *allocatedLen, | |
| LPWSTR *str) | |
| { | |
| DWORD len; | |
| LVITEMW item; | |
| WCHAR dateFmt[80]; /* sufficient for LOCALE_SSHORTDATE */ | |
| WCHAR date[80]; | |
| SYSTEMTIME sysTime; | |
| LPWSTR none; | |
| item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT; | |
| item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0); | |
| item.iSubItem = 0; | |
| item.iImage = 0; | |
| item.lParam = (LPARAM)CertDuplicateCertificateContext(cert); | |
| len = CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, | |
| NULL, 0); | |
| if (len > *allocatedLen) | |
| { | |
| HeapFree(GetProcessHeap(), 0, *str); | |
| *str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
| if (*str) | |
| *allocatedLen = len; | |
| } | |
| if (*str) | |
| { | |
| CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, | |
| *str, len); | |
| item.pszText = *str; | |
| SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item); | |
| } | |
| item.mask = LVIF_TEXT; | |
| len = CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, | |
| CERT_NAME_ISSUER_FLAG, NULL, NULL, 0); | |
| if (len > *allocatedLen) | |
| { | |
| HeapFree(GetProcessHeap(), 0, *str); | |
| *str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
| if (*str) | |
| *allocatedLen = len; | |
| } | |
| if (*str) | |
| { | |
| CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, | |
| CERT_NAME_ISSUER_FLAG, NULL, *str, len); | |
| item.pszText = *str; | |
| item.iSubItem = 1; | |
| SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item); | |
| } | |
| GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SSHORTDATE, dateFmt, | |
| sizeof(dateFmt) / sizeof(dateFmt[0])); | |
| FileTimeToSystemTime(&cert->pCertInfo->NotAfter, &sysTime); | |
| GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, date, | |
| sizeof(date) / sizeof(date[0])); | |
| item.pszText = date; | |
| item.iSubItem = 2; | |
| SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item); | |
| if (!CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, | |
| NULL, &len)) | |
| len = LoadStringW(hInstance, IDS_FRIENDLY_NAME_NONE, (LPWSTR)&none, 0); | |
| if (len > *allocatedLen) | |
| { | |
| HeapFree(GetProcessHeap(), 0, *str); | |
| *str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
| if (*str) | |
| *allocatedLen = len; | |
| } | |
| if (*str) | |
| { | |
| if (!CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, | |
| *str, &len)) | |
| item.pszText = none; | |
| else | |
| item.pszText = *str; | |
| item.iSubItem = 3; | |
| SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item); | |
| } | |
| } | |
| static LPSTR get_cert_mgr_usages(void) | |
| { | |
| static const WCHAR keyName[] = { 'S','o','f','t','w','a','r','e','\\','M', | |
| 'i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r','a', | |
| 'p','h','y','\\','U','I','\\','C','e','r','t','m','g','r','\\','P','u', | |
| 'r','p','o','s','e',0 }; | |
| LPSTR str = NULL; | |
| HKEY key; | |
| if (!RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, NULL, 0, KEY_READ, | |
| NULL, &key, NULL)) | |
| { | |
| LONG rc; | |
| DWORD type, size; | |
| rc = RegQueryValueExA(key, "Purpose", NULL, &type, NULL, &size); | |
| if ((!rc || rc == ERROR_MORE_DATA) && type == REG_SZ) | |
| { | |
| str = HeapAlloc(GetProcessHeap(), 0, size); | |
| if (str) | |
| { | |
| rc = RegQueryValueExA(key, "Purpose", NULL, NULL, (LPBYTE)str, | |
| &size); | |
| if (rc) | |
| { | |
| HeapFree(GetProcessHeap(), 0, str); | |
| str = NULL; | |
| } | |
| } | |
| } | |
| RegCloseKey(key); | |
| } | |
| return str; | |
| } | |
| typedef enum { | |
| PurposeFilterShowAll = 0, | |
| PurposeFilterShowAdvanced = 1, | |
| PurposeFilterShowOID = 2 | |
| } PurposeFilter; | |
| static void initialize_purpose_selection(HWND hwnd) | |
| { | |
| HWND cb = GetDlgItem(hwnd, IDC_MGR_PURPOSE_SELECTION); | |
| WCHAR buf[MAX_STRING_LEN]; | |
| LPSTR usages; | |
| int index; | |
| LoadStringW(hInstance, IDS_PURPOSE_ALL, buf, | |
| sizeof(buf) / sizeof(buf[0])); | |
| index = SendMessageW(cb, CB_INSERTSTRING, -1, (LPARAM)buf); | |
| SendMessageW(cb, CB_SETITEMDATA, index, (LPARAM)PurposeFilterShowAll); | |
| LoadStringW(hInstance, IDS_PURPOSE_ADVANCED, buf, | |
| sizeof(buf) / sizeof(buf[0])); | |
| index = SendMessageW(cb, CB_INSERTSTRING, -1, (LPARAM)buf); | |
| SendMessageW(cb, CB_SETITEMDATA, index, (LPARAM)PurposeFilterShowAdvanced); | |
| SendMessageW(cb, CB_SETCURSEL, 0, 0); | |
| if ((usages = get_cert_mgr_usages())) | |
| { | |
| LPSTR ptr, comma; | |
| for (ptr = usages, comma = strchr(ptr, ','); ptr && *ptr; | |
| ptr = comma ? comma + 1 : NULL, | |
| comma = ptr ? strchr(ptr, ',') : NULL) | |
| { | |
| PCCRYPT_OID_INFO info; | |
| if (comma) | |
| *comma = 0; | |
| if ((info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, ptr, 0))) | |
| { | |
| index = SendMessageW(cb, CB_INSERTSTRING, 0, | |
| (LPARAM)info->pwszName); | |
| SendMessageW(cb, CB_SETITEMDATA, index, (LPARAM)info); | |
| } | |
| } | |
| HeapFree(GetProcessHeap(), 0, usages); | |
| } | |
| } | |
| extern BOOL WINAPI WTHelperGetKnownUsages(DWORD action, | |
| PCCRYPT_OID_INFO **usages); | |
| static CERT_ENHKEY_USAGE *add_oid_to_usage(CERT_ENHKEY_USAGE *usage, LPSTR oid) | |
| { | |
| if (!usage->cUsageIdentifier) | |
| usage->rgpszUsageIdentifier = HeapAlloc(GetProcessHeap(), 0, | |
| sizeof(LPSTR)); | |
| else | |
| usage->rgpszUsageIdentifier = HeapReAlloc(GetProcessHeap(), 0, | |
| usage->rgpszUsageIdentifier, | |
| (usage->cUsageIdentifier + 1) * sizeof(LPSTR)); | |
| if (usage->rgpszUsageIdentifier) | |
| usage->rgpszUsageIdentifier[usage->cUsageIdentifier++] = oid; | |
| else | |
| { | |
| HeapFree(GetProcessHeap(), 0, usage); | |
| usage = NULL; | |
| } | |
| return usage; | |
| } | |
| static CERT_ENHKEY_USAGE *convert_usages_str_to_usage(LPSTR usageStr) | |
| { | |
| CERT_ENHKEY_USAGE *usage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, | |
| sizeof(CERT_ENHKEY_USAGE)); | |
| if (usage) | |
| { | |
| LPSTR ptr, comma; | |
| for (ptr = usageStr, comma = strchr(ptr, ','); usage && ptr && *ptr; | |
| ptr = comma ? comma + 1 : NULL, | |
| comma = ptr ? strchr(ptr, ',') : NULL) | |
| { | |
| if (comma) | |
| *comma = 0; | |
| add_oid_to_usage(usage, ptr); | |
| } | |
| } | |
| return usage; | |
| } | |
| static CERT_ENHKEY_USAGE *create_advanced_filter(void) | |
| { | |
| CERT_ENHKEY_USAGE *advancedUsage = HeapAlloc(GetProcessHeap(), | |
| HEAP_ZERO_MEMORY, sizeof(CERT_ENHKEY_USAGE)); | |
| if (advancedUsage) | |
| { | |
| PCCRYPT_OID_INFO *usages; | |
| if (WTHelperGetKnownUsages(1, &usages)) | |
| { | |
| LPSTR disabledUsagesStr; | |
| if ((disabledUsagesStr = get_cert_mgr_usages())) | |
| { | |
| CERT_ENHKEY_USAGE *disabledUsages = | |
| convert_usages_str_to_usage(disabledUsagesStr); | |
| if (disabledUsages) | |
| { | |
| PCCRYPT_OID_INFO *ptr; | |
| for (ptr = usages; *ptr; ptr++) | |
| { | |
| DWORD i; | |
| BOOL disabled = FALSE; | |
| for (i = 0; !disabled && | |
| i < disabledUsages->cUsageIdentifier; i++) | |
| if (!strcmp(disabledUsages->rgpszUsageIdentifier[i], | |
| (*ptr)->pszOID)) | |
| disabled = TRUE; | |
| if (!disabled) | |
| add_oid_to_usage(advancedUsage, | |
| (LPSTR)(*ptr)->pszOID); | |
| } | |
| /* The individual strings are pointers to disabledUsagesStr, | |
| * so they're freed when it is. | |
| */ | |
| HeapFree(GetProcessHeap(), 0, | |
| disabledUsages->rgpszUsageIdentifier); | |
| HeapFree(GetProcessHeap(), 0, disabledUsages); | |
| } | |
| HeapFree(GetProcessHeap(), 0, disabledUsagesStr); | |
| } | |
| WTHelperGetKnownUsages(2, &usages); | |
| } | |
| } | |
| return advancedUsage; | |
| } | |
| static int CALLBACK cert_mgr_sort_by_subject(LPARAM lp1, LPARAM lp2, LPARAM lp); | |
| static void show_store_certs(HWND hwnd, HCERTSTORE store) | |
| { | |
| HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS); | |
| HWND cb = GetDlgItem(hwnd, IDC_MGR_PURPOSE_SELECTION); | |
| PCCERT_CONTEXT cert = NULL; | |
| DWORD allocatedLen = 0; | |
| LPWSTR str = NULL; | |
| int index; | |
| PurposeFilter filter = PurposeFilterShowAll; | |
| LPCSTR oid = NULL; | |
| CERT_ENHKEY_USAGE *advanced = NULL; | |
| index = SendMessageW(cb, CB_GETCURSEL, 0, 0); | |
| if (index >= 0) | |
| { | |
| INT_PTR data = SendMessageW(cb, CB_GETITEMDATA, index, 0); | |
| if (!HIWORD(data)) | |
| filter = data; | |
| else | |
| { | |
| PCCRYPT_OID_INFO info = (PCCRYPT_OID_INFO)data; | |
| filter = PurposeFilterShowOID; | |
| oid = info->pszOID; | |
| } | |
| } | |
| if (filter == PurposeFilterShowAdvanced) | |
| advanced = create_advanced_filter(); | |
| do { | |
| cert = CertEnumCertificatesInStore(store, cert); | |
| if (cert) | |
| { | |
| BOOL show = FALSE; | |
| if (filter == PurposeFilterShowAll) | |
| show = TRUE; | |
| else | |
| { | |
| int numOIDs; | |
| DWORD cbOIDs = 0; | |
| if (CertGetValidUsages(1, &cert, &numOIDs, NULL, &cbOIDs)) | |
| { | |
| if (numOIDs == -1) | |
| { | |
| /* -1 implies all usages are valid */ | |
| show = TRUE; | |
| } | |
| else | |
| { | |
| LPSTR *oids = HeapAlloc(GetProcessHeap(), 0, cbOIDs); | |
| if (oids) | |
| { | |
| if (CertGetValidUsages(1, &cert, &numOIDs, oids, | |
| &cbOIDs)) | |
| { | |
| int i; | |
| if (filter == PurposeFilterShowOID) | |
| { | |
| for (i = 0; !show && i < numOIDs; i++) | |
| if (!strcmp(oids[i], oid)) | |
| show = TRUE; | |
| } | |
| else | |
| { | |
| for (i = 0; !show && i < numOIDs; i++) | |
| { | |
| DWORD j; | |
| for (j = 0; !show && | |
| j < advanced->cUsageIdentifier; j++) | |
| if (!strcmp(oids[i], | |
| advanced->rgpszUsageIdentifier[j])) | |
| show = TRUE; | |
| } | |
| } | |
| } | |
| HeapFree(GetProcessHeap(), 0, oids); | |
| } | |
| } | |
| } | |
| } | |
| if (show) | |
| add_cert_to_view(lv, cert, &allocatedLen, &str); | |
| } | |
| } while (cert); | |
| HeapFree(GetProcessHeap(), 0, str); | |
| if (advanced) | |
| { | |
| HeapFree(GetProcessHeap(), 0, advanced->rgpszUsageIdentifier); | |
| HeapFree(GetProcessHeap(), 0, advanced); | |
| } | |
| SendMessageW(lv, LVM_SORTITEMSEX, (WPARAM)lv, | |
| (LPARAM)cert_mgr_sort_by_subject); | |
| } | |
| static const WCHAR my[] = { 'M','y',0 }; | |
| static const WCHAR addressBook[] = { | |
| 'A','d','d','r','e','s','s','B','o','o','k',0 }; | |
| static const WCHAR ca[] = { 'C','A',0 }; | |
| static const WCHAR root[] = { 'R','o','o','t',0 }; | |
| static const WCHAR trustedPublisher[] = { | |
| 'T','r','u','s','t','e','d','P','u','b','l','i','s','h','e','r',0 }; | |
| static const WCHAR disallowed[] = { 'D','i','s','a','l','l','o','w','e','d',0 }; | |
| struct CertMgrStoreInfo | |
| { | |
| LPCWSTR name; | |
| int removeWarning; | |
| int removePluralWarning; | |
| }; | |
| static const struct CertMgrStoreInfo defaultStoreList[] = { | |
| { my, IDS_WARN_REMOVE_MY, IDS_WARN_REMOVE_PLURAL_MY }, | |
| { addressBook, IDS_WARN_REMOVE_ADDRESSBOOK, | |
| IDS_WARN_REMOVE_PLURAL_ADDRESSBOOK }, | |
| { ca, IDS_WARN_REMOVE_CA, IDS_WARN_REMOVE_PLURAL_CA }, | |
| { root, IDS_WARN_REMOVE_ROOT, IDS_WARN_REMOVE_PLURAL_ROOT }, | |
| { trustedPublisher, IDS_WARN_REMOVE_TRUSTEDPUBLISHER, | |
| IDS_WARN_REMOVE_PLURAL_TRUSTEDPUBLISHER }, | |
| { disallowed, IDS_WARN_REMOVE_DEFAULT }, | |
| }; | |
| static const struct CertMgrStoreInfo publisherStoreList[] = { | |
| { root, IDS_WARN_REMOVE_ROOT, IDS_WARN_REMOVE_PLURAL_ROOT }, | |
| { trustedPublisher, IDS_WARN_REMOVE_TRUSTEDPUBLISHER, | |
| IDS_WARN_REMOVE_PLURAL_TRUSTEDPUBLISHER }, | |
| { disallowed, IDS_WARN_REMOVE_PLURAL_DEFAULT }, | |
| }; | |
| struct CertMgrData | |
| { | |
| HIMAGELIST imageList; | |
| LPCWSTR title; | |
| DWORD nStores; | |
| const struct CertMgrStoreInfo *stores; | |
| }; | |
| static void show_cert_stores(HWND hwnd, DWORD dwFlags, struct CertMgrData *data) | |
| { | |
| const struct CertMgrStoreInfo *storeList; | |
| int cStores, i; | |
| HWND tab = GetDlgItem(hwnd, IDC_MGR_STORES); | |
| if (dwFlags & CRYPTUI_CERT_MGR_PUBLISHER_TAB) | |
| { | |
| storeList = publisherStoreList; | |
| cStores = sizeof(publisherStoreList) / sizeof(publisherStoreList[0]); | |
| } | |
| else | |
| { | |
| storeList = defaultStoreList; | |
| cStores = sizeof(defaultStoreList) / sizeof(defaultStoreList[0]); | |
| } | |
| if (dwFlags & CRYPTUI_CERT_MGR_SINGLE_TAB_FLAG) | |
| cStores = 1; | |
| data->nStores = cStores; | |
| data->stores = storeList; | |
| for (i = 0; i < cStores; i++) | |
| { | |
| LPCWSTR name; | |
| TCITEMW item; | |
| HCERTSTORE store; | |
| if (!(name = CryptFindLocalizedName(storeList[i].name))) | |
| name = storeList[i].name; | |
| store = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0, | |
| CERT_SYSTEM_STORE_CURRENT_USER, storeList[i].name); | |
| item.mask = TCIF_TEXT | TCIF_PARAM; | |
| item.pszText = (LPWSTR)name; | |
| item.lParam = (LPARAM)store; | |
| SendMessageW(tab, TCM_INSERTITEMW, i, (LPARAM)&item); | |
| } | |
| } | |
| static void free_certs(HWND lv) | |
| { | |
| LVITEMW item; | |
| int items = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i; | |
| for (i = 0; i < items; i++) | |
| { | |
| item.mask = LVIF_PARAM; | |
| item.iItem = i; | |
| item.iSubItem = 0; | |
| SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item); | |
| CertFreeCertificateContext((PCCERT_CONTEXT)item.lParam); | |
| } | |
| } | |
| static HCERTSTORE cert_mgr_index_to_store(HWND tab, int index) | |
| { | |
| TCITEMW item; | |
| item.mask = TCIF_PARAM; | |
| SendMessageW(tab, TCM_GETITEMW, index, (LPARAM)&item); | |
| return (HCERTSTORE)item.lParam; | |
| } | |
| static HCERTSTORE cert_mgr_current_store(HWND hwnd) | |
| { | |
| HWND tab = GetDlgItem(hwnd, IDC_MGR_STORES); | |
| return cert_mgr_index_to_store(tab, SendMessageW(tab, TCM_GETCURSEL, 0, 0)); | |
| } | |
| static void close_stores(HWND tab) | |
| { | |
| int i, tabs = SendMessageW(tab, TCM_GETITEMCOUNT, 0, 0); | |
| for (i = 0; i < tabs; i++) | |
| CertCloseStore(cert_mgr_index_to_store(tab, i), 0); | |
| } | |
| static void refresh_store_certs(HWND hwnd) | |
| { | |
| HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS); | |
| free_certs(lv); | |
| SendMessageW(lv, LVM_DELETEALLITEMS, 0, 0); | |
| show_store_certs(hwnd, cert_mgr_current_store(hwnd)); | |
| } | |
| typedef enum { | |
| CheckBitmapIndexUnchecked = 1, | |
| CheckBitmapIndexChecked = 2, | |
| CheckBitmapIndexDisabledUnchecked = 3, | |
| CheckBitmapIndexDisabledChecked = 4 | |
| } CheckBitmapIndex; | |
| static void add_known_usage(HWND lv, PCCRYPT_OID_INFO info, | |
| CheckBitmapIndex state) | |
| { | |
| LVITEMW item; | |
| item.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM; | |
| item.state = INDEXTOSTATEIMAGEMASK(state); | |
| item.stateMask = LVIS_STATEIMAGEMASK; | |
| item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0); | |
| item.iSubItem = 0; | |
| item.lParam = (LPARAM)info; | |
| item.pszText = (LPWSTR)info->pwszName; | |
| SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item); | |
| } | |
| static void add_known_usages_to_list(HWND lv, CheckBitmapIndex state) | |
| { | |
| PCCRYPT_OID_INFO *usages; | |
| if (WTHelperGetKnownUsages(1, &usages)) | |
| { | |
| PCCRYPT_OID_INFO *ptr; | |
| for (ptr = usages; *ptr; ptr++) | |
| add_known_usage(lv, *ptr, state); | |
| WTHelperGetKnownUsages(2, &usages); | |
| } | |
| } | |
| static void toggle_usage(HWND hwnd, int iItem) | |
| { | |
| LVITEMW item; | |
| int res; | |
| HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES); | |
| item.mask = LVIF_STATE; | |
| item.iItem = iItem; | |
| item.iSubItem = 0; | |
| item.stateMask = LVIS_STATEIMAGEMASK; | |
| res = SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item); | |
| if (res) | |
| { | |
| int state = item.state >> 12; | |
| item.state = INDEXTOSTATEIMAGEMASK( | |
| state == CheckBitmapIndexChecked ? CheckBitmapIndexUnchecked : | |
| CheckBitmapIndexChecked); | |
| SendMessageW(lv, LVM_SETITEMSTATE, iItem, (LPARAM)&item); | |
| } | |
| } | |
| static LONG_PTR find_oid_in_list(HWND lv, LPCSTR oid) | |
| { | |
| PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, | |
| (void *)oid, CRYPT_ENHKEY_USAGE_OID_GROUP_ID); | |
| LONG_PTR ret; | |
| if (oidInfo) | |
| { | |
| LVFINDINFOW findInfo; | |
| findInfo.flags = LVFI_PARAM; | |
| findInfo.lParam = (LPARAM)oidInfo; | |
| ret = SendMessageW(lv, LVM_FINDITEMW, -1, (LPARAM)&findInfo); | |
| } | |
| else | |
| { | |
| LVFINDINFOA findInfo; | |
| findInfo.flags = LVFI_STRING; | |
| findInfo.psz = oid; | |
| ret = SendMessageW(lv, LVM_FINDITEMA, -1, (LPARAM)&findInfo); | |
| } | |
| return ret; | |
| } | |
| static void save_cert_mgr_usages(HWND hwnd) | |
| { | |
| static const WCHAR keyName[] = { 'S','o','f','t','w','a','r','e','\\','M', | |
| 'i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r','a', | |
| 'p','h','y','\\','U','I','\\','C','e','r','t','m','g','r','\\','P','u', | |
| 'r','p','o','s','e',0 }; | |
| HKEY key; | |
| HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES); | |
| int purposes = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i; | |
| LVITEMW item; | |
| LPSTR str = NULL; | |
| item.mask = LVIF_STATE | LVIF_PARAM; | |
| item.iSubItem = 0; | |
| item.stateMask = LVIS_STATEIMAGEMASK; | |
| for (i = 0; i < purposes; i++) | |
| { | |
| item.iItem = i; | |
| if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item)) | |
| { | |
| int state = item.state >> 12; | |
| if (state == CheckBitmapIndexUnchecked) | |
| { | |
| CRYPT_OID_INFO *info = (CRYPT_OID_INFO *)item.lParam; | |
| BOOL firstString = TRUE; | |
| if (!str) | |
| str = HeapAlloc(GetProcessHeap(), 0, | |
| strlen(info->pszOID) + 1); | |
| else | |
| { | |
| str = HeapReAlloc(GetProcessHeap(), 0, str, | |
| strlen(str) + 1 + strlen(info->pszOID) + 1); | |
| firstString = FALSE; | |
| } | |
| if (str) | |
| { | |
| LPSTR ptr = firstString ? str : str + strlen(str); | |
| if (!firstString) | |
| *ptr++ = ','; | |
| strcpy(ptr, info->pszOID); | |
| } | |
| } | |
| } | |
| } | |
| if (!RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, NULL, 0, KEY_ALL_ACCESS, | |
| NULL, &key, NULL)) | |
| { | |
| if (str) | |
| RegSetValueExA(key, "Purpose", 0, REG_SZ, (const BYTE *)str, | |
| strlen(str) + 1); | |
| else | |
| RegDeleteValueA(key, "Purpose"); | |
| RegCloseKey(key); | |
| } | |
| HeapFree(GetProcessHeap(), 0, str); | |
| } | |
| static LRESULT CALLBACK cert_mgr_advanced_dlg_proc(HWND hwnd, UINT msg, | |
| WPARAM wp, LPARAM lp) | |
| { | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| RECT rc; | |
| LVCOLUMNW column; | |
| HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES); | |
| HIMAGELIST imageList; | |
| LPSTR disabledUsages; | |
| GetWindowRect(lv, &rc); | |
| column.mask = LVCF_WIDTH; | |
| column.cx = rc.right - rc.left; | |
| SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column); | |
| imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 4, 0); | |
| if (imageList) | |
| { | |
| HBITMAP bmp; | |
| COLORREF backColor = RGB(255, 0, 255); | |
| bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_CHECKS)); | |
| ImageList_AddMasked(imageList, bmp, backColor); | |
| DeleteObject(bmp); | |
| ImageList_SetBkColor(imageList, CLR_NONE); | |
| SendMessageW(lv, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM)imageList); | |
| SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)imageList); | |
| } | |
| add_known_usages_to_list(lv, CheckBitmapIndexChecked); | |
| if ((disabledUsages = get_cert_mgr_usages())) | |
| { | |
| LPSTR ptr, comma; | |
| for (ptr = disabledUsages, comma = strchr(ptr, ','); ptr && *ptr; | |
| ptr = comma ? comma + 1 : NULL, | |
| comma = ptr ? strchr(ptr, ',') : NULL) | |
| { | |
| LONG_PTR index; | |
| if (comma) | |
| *comma = 0; | |
| if ((index = find_oid_in_list(lv, ptr)) != -1) | |
| toggle_usage(hwnd, index); | |
| } | |
| HeapFree(GetProcessHeap(), 0, disabledUsages); | |
| } | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| NMITEMACTIVATE *nm; | |
| switch (hdr->code) | |
| { | |
| case NM_CLICK: | |
| nm = (NMITEMACTIVATE *)lp; | |
| toggle_usage(hwnd, nm->iItem); | |
| SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0); | |
| break; | |
| } | |
| break; | |
| } | |
| case WM_COMMAND: | |
| switch (wp) | |
| { | |
| case IDOK: | |
| save_cert_mgr_usages(hwnd); | |
| ImageList_Destroy((HIMAGELIST)GetWindowLongPtrW(hwnd, DWLP_USER)); | |
| EndDialog(hwnd, IDOK); | |
| break; | |
| case IDCANCEL: | |
| ImageList_Destroy((HIMAGELIST)GetWindowLongPtrW(hwnd, DWLP_USER)); | |
| EndDialog(hwnd, IDCANCEL); | |
| break; | |
| } | |
| break; | |
| } | |
| return 0; | |
| } | |
| static void cert_mgr_clear_cert_selection(HWND hwnd) | |
| { | |
| WCHAR empty[] = { 0 }; | |
| EnableWindow(GetDlgItem(hwnd, IDC_MGR_EXPORT), FALSE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_MGR_REMOVE), FALSE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_MGR_VIEW), FALSE); | |
| SendMessageW(GetDlgItem(hwnd, IDC_MGR_PURPOSES), WM_SETTEXT, 0, | |
| (LPARAM)empty); | |
| refresh_store_certs(hwnd); | |
| } | |
| static PCCERT_CONTEXT cert_mgr_index_to_cert(HWND hwnd, int index) | |
| { | |
| PCCERT_CONTEXT cert = NULL; | |
| LVITEMW item; | |
| item.mask = LVIF_PARAM; | |
| item.iItem = index; | |
| item.iSubItem = 0; | |
| if (SendMessageW(GetDlgItem(hwnd, IDC_MGR_CERTS), LVM_GETITEMW, 0, | |
| (LPARAM)&item)) | |
| cert = (PCCERT_CONTEXT)item.lParam; | |
| return cert; | |
| } | |
| static void show_selected_cert(HWND hwnd, int index) | |
| { | |
| PCCERT_CONTEXT cert = cert_mgr_index_to_cert(hwnd, index); | |
| if (cert) | |
| { | |
| CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo; | |
| memset(&viewInfo, 0, sizeof(viewInfo)); | |
| viewInfo.dwSize = sizeof(viewInfo); | |
| viewInfo.hwndParent = hwnd; | |
| viewInfo.pCertContext = cert; | |
| /* FIXME: this should be modal */ | |
| CryptUIDlgViewCertificateW(&viewInfo, NULL); | |
| } | |
| } | |
| static void cert_mgr_show_cert_usages(HWND hwnd, int index) | |
| { | |
| HWND text = GetDlgItem(hwnd, IDC_MGR_PURPOSES); | |
| PCCERT_CONTEXT cert = cert_mgr_index_to_cert(hwnd, index); | |
| PCERT_ENHKEY_USAGE usage; | |
| DWORD size; | |
| /* Get enhanced key usage. Have to check for a property and an extension | |
| * separately, because CertGetEnhancedKeyUsage will succeed and return an | |
| * empty usage if neither is set. Unfortunately an empty usage implies | |
| * no usage is allowed, so we have to distinguish between the two cases. | |
| */ | |
| if (CertGetEnhancedKeyUsage(cert, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, | |
| NULL, &size)) | |
| { | |
| usage = HeapAlloc(GetProcessHeap(), 0, size); | |
| if (!CertGetEnhancedKeyUsage(cert, | |
| CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, usage, &size)) | |
| { | |
| HeapFree(GetProcessHeap(), 0, usage); | |
| usage = NULL; | |
| } | |
| } | |
| else if (CertGetEnhancedKeyUsage(cert, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, | |
| NULL, &size)) | |
| { | |
| usage = HeapAlloc(GetProcessHeap(), 0, size); | |
| if (!CertGetEnhancedKeyUsage(cert, | |
| CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, usage, &size)) | |
| { | |
| HeapFree(GetProcessHeap(), 0, usage); | |
| usage = NULL; | |
| } | |
| } | |
| else | |
| usage = NULL; | |
| if (usage) | |
| { | |
| if (usage->cUsageIdentifier) | |
| { | |
| static const WCHAR commaSpace[] = { ',',' ',0 }; | |
| DWORD i, len = 1; | |
| LPWSTR str, ptr; | |
| for (i = 0; i < usage->cUsageIdentifier; i++) | |
| { | |
| PCCRYPT_OID_INFO info = | |
| CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, | |
| usage->rgpszUsageIdentifier[i], | |
| CRYPT_ENHKEY_USAGE_OID_GROUP_ID); | |
| if (info) | |
| len += strlenW(info->pwszName); | |
| else | |
| len += strlen(usage->rgpszUsageIdentifier[i]); | |
| if (i < usage->cUsageIdentifier - 1) | |
| len += strlenW(commaSpace); | |
| } | |
| str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
| if (str) | |
| { | |
| for (i = 0, ptr = str; i < usage->cUsageIdentifier; i++) | |
| { | |
| PCCRYPT_OID_INFO info = | |
| CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, | |
| usage->rgpszUsageIdentifier[i], | |
| CRYPT_ENHKEY_USAGE_OID_GROUP_ID); | |
| if (info) | |
| { | |
| strcpyW(ptr, info->pwszName); | |
| ptr += strlenW(info->pwszName); | |
| } | |
| else | |
| { | |
| LPCSTR src = usage->rgpszUsageIdentifier[i]; | |
| for (; *src; ptr++, src++) | |
| *ptr = *src; | |
| *ptr = 0; | |
| } | |
| if (i < usage->cUsageIdentifier - 1) | |
| { | |
| strcpyW(ptr, commaSpace); | |
| ptr += strlenW(commaSpace); | |
| } | |
| } | |
| *ptr = 0; | |
| SendMessageW(text, WM_SETTEXT, 0, (LPARAM)str); | |
| HeapFree(GetProcessHeap(), 0, str); | |
| } | |
| HeapFree(GetProcessHeap(), 0, usage); | |
| } | |
| else | |
| { | |
| WCHAR buf[MAX_STRING_LEN]; | |
| LoadStringW(hInstance, IDS_ALLOWED_PURPOSE_NONE, buf, | |
| sizeof(buf) / sizeof(buf[0])); | |
| SendMessageW(text, WM_SETTEXT, 0, (LPARAM)buf); | |
| } | |
| } | |
| else | |
| { | |
| WCHAR buf[MAX_STRING_LEN]; | |
| LoadStringW(hInstance, IDS_ALLOWED_PURPOSE_ALL, buf, | |
| sizeof(buf) / sizeof(buf[0])); | |
| SendMessageW(text, WM_SETTEXT, 0, (LPARAM)buf); | |
| } | |
| } | |
| static void cert_mgr_do_remove(HWND hwnd) | |
| { | |
| int tabIndex = SendMessageW(GetDlgItem(hwnd, IDC_MGR_STORES), | |
| TCM_GETCURSEL, 0, 0); | |
| struct CertMgrData *data = | |
| (struct CertMgrData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if (tabIndex < data->nStores) | |
| { | |
| HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS); | |
| WCHAR warning[MAX_STRING_LEN], title[MAX_STRING_LEN]; | |
| LPCWSTR pTitle; | |
| int warningID; | |
| if (SendMessageW(lv, LVM_GETSELECTEDCOUNT, 0, 0) > 1) | |
| warningID = data->stores[tabIndex].removePluralWarning; | |
| else | |
| warningID = data->stores[tabIndex].removeWarning; | |
| if (data->title) | |
| pTitle = data->title; | |
| else | |
| { | |
| LoadStringW(hInstance, IDS_CERT_MGR, title, | |
| sizeof(title) / sizeof(title[0])); | |
| pTitle = title; | |
| } | |
| LoadStringW(hInstance, warningID, warning, | |
| sizeof(warning) / sizeof(warning[0])); | |
| if (MessageBoxW(hwnd, warning, pTitle, MB_YESNO) == IDYES) | |
| { | |
| int selection = -1; | |
| do { | |
| selection = SendMessageW(lv, LVM_GETNEXTITEM, selection, | |
| LVNI_SELECTED); | |
| if (selection >= 0) | |
| { | |
| PCCERT_CONTEXT cert = cert_mgr_index_to_cert(hwnd, | |
| selection); | |
| CertDeleteCertificateFromStore(cert); | |
| } | |
| } while (selection >= 0); | |
| cert_mgr_clear_cert_selection(hwnd); | |
| } | |
| } | |
| } | |
| static void cert_mgr_do_export(HWND hwnd) | |
| { | |
| HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS); | |
| int selectionCount = SendMessageW(lv, LVM_GETSELECTEDCOUNT, 0, 0); | |
| if (selectionCount == 1) | |
| { | |
| int selection = SendMessageW(lv, LVM_GETNEXTITEM, -1, | |
| LVNI_SELECTED); | |
| if (selection >= 0) | |
| { | |
| PCCERT_CONTEXT cert = cert_mgr_index_to_cert(hwnd, selection); | |
| if (cert) | |
| { | |
| CRYPTUI_WIZ_EXPORT_INFO info; | |
| info.dwSize = sizeof(info); | |
| info.pwszExportFileName = NULL; | |
| info.dwSubjectChoice = CRYPTUI_WIZ_EXPORT_CERT_CONTEXT; | |
| info.u.pCertContext = cert; | |
| info.cStores = 0; | |
| CryptUIWizExport(0, hwnd, NULL, &info, NULL); | |
| } | |
| } | |
| } | |
| else if (selectionCount > 1) | |
| { | |
| HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, | |
| CERT_STORE_CREATE_NEW_FLAG, NULL); | |
| if (store) | |
| { | |
| CRYPTUI_WIZ_EXPORT_INFO info; | |
| int selection = -1; | |
| info.dwSize = sizeof(info); | |
| info.pwszExportFileName = NULL; | |
| info.dwSubjectChoice = | |
| CRYPTUI_WIZ_EXPORT_CERT_STORE_CERTIFICATES_ONLY; | |
| info.u.hCertStore = store; | |
| info.cStores = 0; | |
| do { | |
| selection = SendMessageW(lv, LVM_GETNEXTITEM, selection, | |
| LVNI_SELECTED); | |
| if (selection >= 0) | |
| { | |
| PCCERT_CONTEXT cert = cert_mgr_index_to_cert(hwnd, | |
| selection); | |
| CertAddCertificateContextToStore(store, cert, | |
| CERT_STORE_ADD_ALWAYS, NULL); | |
| } | |
| } while (selection >= 0); | |
| CryptUIWizExport(0, hwnd, NULL, &info, NULL); | |
| CertCloseStore(store, 0); | |
| } | |
| } | |
| } | |
| static int cert_mgr_sort_by_text(HWND lv, int col, int index1, int index2) | |
| { | |
| LVITEMW item; | |
| WCHAR buf1[MAX_STRING_LEN]; | |
| WCHAR buf2[MAX_STRING_LEN]; | |
| item.cchTextMax = sizeof(buf1) / sizeof(buf1[0]); | |
| item.mask = LVIF_TEXT; | |
| item.pszText = buf1; | |
| item.iItem = index1; | |
| item.iSubItem = col; | |
| SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item); | |
| item.pszText = buf2; | |
| item.iItem = index2; | |
| SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item); | |
| return strcmpW(buf1, buf2); | |
| } | |
| static int CALLBACK cert_mgr_sort_by_subject(LPARAM lp1, LPARAM lp2, LPARAM lp) | |
| { | |
| return cert_mgr_sort_by_text((HWND)lp, 0, lp1, lp2); | |
| } | |
| static int CALLBACK cert_mgr_sort_by_issuer(LPARAM lp1, LPARAM lp2, LPARAM lp) | |
| { | |
| return cert_mgr_sort_by_text((HWND)lp, 1, lp1, lp2); | |
| } | |
| static int CALLBACK cert_mgr_sort_by_date(LPARAM lp1, LPARAM lp2, LPARAM lp) | |
| { | |
| PCCERT_CONTEXT cert1 = (PCCERT_CONTEXT)lp1; | |
| PCCERT_CONTEXT cert2 = (PCCERT_CONTEXT)lp2; | |
| return CompareFileTime(&cert1->pCertInfo->NotAfter, | |
| &cert2->pCertInfo->NotAfter); | |
| } | |
| static int CALLBACK cert_mgr_sort_by_friendly_name(LPARAM lp1, LPARAM lp2, | |
| LPARAM lp) | |
| { | |
| return cert_mgr_sort_by_text((HWND)lp, 3, lp1, lp2); | |
| } | |
| static LRESULT CALLBACK cert_mgr_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| struct CertMgrData *data; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| PCCRYPTUI_CERT_MGR_STRUCT pCryptUICertMgr = | |
| (PCCRYPTUI_CERT_MGR_STRUCT)lp; | |
| HWND tab = GetDlgItem(hwnd, IDC_MGR_STORES); | |
| data = HeapAlloc(GetProcessHeap(), 0, sizeof(struct CertMgrData)); | |
| if (!data) | |
| return 0; | |
| data->imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 2, 0); | |
| if (data->imageList) | |
| { | |
| HBITMAP bmp; | |
| COLORREF backColor = RGB(255, 0, 255); | |
| bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_SMALL_ICONS)); | |
| ImageList_AddMasked(data->imageList, bmp, backColor); | |
| DeleteObject(bmp); | |
| ImageList_SetBkColor(data->imageList, CLR_NONE); | |
| SendMessageW(GetDlgItem(hwnd, IDC_MGR_CERTS), LVM_SETIMAGELIST, | |
| LVSIL_SMALL, (LPARAM)data->imageList); | |
| } | |
| SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); | |
| data->title = pCryptUICertMgr->pwszTitle; | |
| initialize_purpose_selection(hwnd); | |
| add_cert_columns(hwnd); | |
| if (pCryptUICertMgr->pwszTitle) | |
| SendMessageW(hwnd, WM_SETTEXT, 0, | |
| (LPARAM)pCryptUICertMgr->pwszTitle); | |
| show_cert_stores(hwnd, pCryptUICertMgr->dwFlags, data); | |
| show_store_certs(hwnd, cert_mgr_index_to_store(tab, 0)); | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| switch (hdr->code) | |
| { | |
| case TCN_SELCHANGE: | |
| cert_mgr_clear_cert_selection(hwnd); | |
| break; | |
| case LVN_ITEMCHANGED: | |
| { | |
| NMITEMACTIVATE *nm; | |
| HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS); | |
| nm = (NMITEMACTIVATE*)lp; | |
| if (nm->uNewState & LVN_ITEMACTIVATE) | |
| { | |
| int numSelected = SendMessageW(lv, LVM_GETSELECTEDCOUNT, 0, 0); | |
| EnableWindow(GetDlgItem(hwnd, IDC_MGR_EXPORT), numSelected > 0); | |
| EnableWindow(GetDlgItem(hwnd, IDC_MGR_REMOVE), numSelected > 0); | |
| EnableWindow(GetDlgItem(hwnd, IDC_MGR_VIEW), numSelected == 1); | |
| if (numSelected == 1) | |
| cert_mgr_show_cert_usages(hwnd, nm->iItem); | |
| } | |
| break; | |
| } | |
| case NM_DBLCLK: | |
| show_selected_cert(hwnd, ((NMITEMACTIVATE *)lp)->iItem); | |
| break; | |
| case LVN_KEYDOWN: | |
| { | |
| NMLVKEYDOWN *lvk = (NMLVKEYDOWN *)lp; | |
| if (lvk->wVKey == VK_DELETE) | |
| cert_mgr_do_remove(hwnd); | |
| break; | |
| } | |
| case LVN_COLUMNCLICK: | |
| { | |
| NMLISTVIEW *nmlv = (NMLISTVIEW *)lp; | |
| HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS); | |
| /* FIXME: doesn't support swapping sort order between ascending | |
| * and descending. | |
| */ | |
| switch (nmlv->iSubItem) | |
| { | |
| case 0: | |
| SendMessageW(lv, LVM_SORTITEMSEX, (WPARAM)lv, | |
| (LPARAM)cert_mgr_sort_by_subject); | |
| break; | |
| case 1: | |
| SendMessageW(lv, LVM_SORTITEMSEX, (WPARAM)lv, | |
| (LPARAM)cert_mgr_sort_by_issuer); | |
| break; | |
| case 2: | |
| SendMessageW(lv, LVM_SORTITEMS, 0, | |
| (LPARAM)cert_mgr_sort_by_date); | |
| break; | |
| case 3: | |
| SendMessageW(lv, LVM_SORTITEMSEX, (WPARAM)lv, | |
| (LPARAM)cert_mgr_sort_by_friendly_name); | |
| break; | |
| } | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| case WM_COMMAND: | |
| switch (wp) | |
| { | |
| case ((CBN_SELCHANGE << 16) | IDC_MGR_PURPOSE_SELECTION): | |
| cert_mgr_clear_cert_selection(hwnd); | |
| break; | |
| case IDC_MGR_IMPORT: | |
| if (CryptUIWizImport(0, hwnd, NULL, NULL, | |
| cert_mgr_current_store(hwnd))) | |
| refresh_store_certs(hwnd); | |
| break; | |
| case IDC_MGR_ADVANCED: | |
| if (DialogBoxW(hInstance, MAKEINTRESOURCEW(IDD_CERT_MGR_ADVANCED), | |
| hwnd, cert_mgr_advanced_dlg_proc) == IDOK) | |
| { | |
| HWND cb = GetDlgItem(hwnd, IDC_MGR_PURPOSE_SELECTION); | |
| int index, len; | |
| LPWSTR curString = NULL; | |
| index = SendMessageW(cb, CB_GETCURSEL, 0, 0); | |
| if (index >= 0) | |
| { | |
| len = SendMessageW(cb, CB_GETLBTEXTLEN, index, 0); | |
| curString = HeapAlloc(GetProcessHeap(), 0, | |
| (len + 1) * sizeof(WCHAR)); | |
| SendMessageW(cb, CB_GETLBTEXT, index, (LPARAM)curString); | |
| } | |
| SendMessageW(cb, CB_RESETCONTENT, 0, 0); | |
| initialize_purpose_selection(hwnd); | |
| if (curString) | |
| { | |
| index = SendMessageW(cb, CB_FINDSTRINGEXACT, -1, | |
| (LPARAM)curString); | |
| if (index >= 0) | |
| SendMessageW(cb, CB_SETCURSEL, index, 0); | |
| HeapFree(GetProcessHeap(), 0, curString); | |
| } | |
| refresh_store_certs(hwnd); | |
| } | |
| break; | |
| case IDC_MGR_VIEW: | |
| { | |
| HWND lv = GetDlgItem(hwnd, IDC_MGR_CERTS); | |
| int selection = SendMessageW(lv, LVM_GETNEXTITEM, -1, | |
| LVNI_SELECTED); | |
| if (selection >= 0) | |
| show_selected_cert(hwnd, selection); | |
| break; | |
| } | |
| case IDC_MGR_EXPORT: | |
| cert_mgr_do_export(hwnd); | |
| break; | |
| case IDC_MGR_REMOVE: | |
| cert_mgr_do_remove(hwnd); | |
| break; | |
| case IDCANCEL: | |
| free_certs(GetDlgItem(hwnd, IDC_MGR_CERTS)); | |
| close_stores(GetDlgItem(hwnd, IDC_MGR_STORES)); | |
| close_stores(GetDlgItem(hwnd, IDC_MGR_STORES)); | |
| data = (struct CertMgrData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| ImageList_Destroy(data->imageList); | |
| HeapFree(GetProcessHeap(), 0, data); | |
| EndDialog(hwnd, IDCANCEL); | |
| break; | |
| } | |
| break; | |
| } | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * CryptUIDlgCertMgr (CRYPTUI.@) | |
| */ | |
| BOOL WINAPI CryptUIDlgCertMgr(PCCRYPTUI_CERT_MGR_STRUCT pCryptUICertMgr) | |
| { | |
| TRACE("(%p)\n", pCryptUICertMgr); | |
| if (pCryptUICertMgr->dwSize != sizeof(CRYPTUI_CERT_MGR_STRUCT)) | |
| { | |
| WARN("unexpected size %d\n", pCryptUICertMgr->dwSize); | |
| SetLastError(E_INVALIDARG); | |
| return FALSE; | |
| } | |
| DialogBoxParamW(hInstance, MAKEINTRESOURCEW(IDD_CERT_MGR), | |
| pCryptUICertMgr->hwndParent, cert_mgr_dlg_proc, (LPARAM)pCryptUICertMgr); | |
| return TRUE; | |
| } | |
| /* FIXME: real names are unknown, functions are undocumented */ | |
| typedef struct _CRYPTUI_ENUM_SYSTEM_STORE_ARGS | |
| { | |
| DWORD dwFlags; | |
| void *pvSystemStoreLocationPara; | |
| } CRYPTUI_ENUM_SYSTEM_STORE_ARGS, *PCRYPTUI_ENUM_SYSTEM_STORE_ARGS; | |
| typedef struct _CRYPTUI_ENUM_DATA | |
| { | |
| DWORD cStores; | |
| HCERTSTORE *rghStore; | |
| DWORD cEnumArgs; | |
| PCRYPTUI_ENUM_SYSTEM_STORE_ARGS rgEnumArgs; | |
| } CRYPTUI_ENUM_DATA, *PCRYPTUI_ENUM_DATA; | |
| typedef BOOL (WINAPI *PFN_SELECTED_STORE_CB)(HCERTSTORE store, HWND hwnd, | |
| void *pvArg); | |
| /* Values for dwFlags */ | |
| #define CRYPTUI_ENABLE_SHOW_PHYSICAL_STORE 0x00000001 | |
| typedef struct _CRYPTUI_SELECTSTORE_INFO_A | |
| { | |
| DWORD dwSize; | |
| HWND parent; | |
| DWORD dwFlags; | |
| LPSTR pszTitle; | |
| LPSTR pszText; | |
| CRYPTUI_ENUM_DATA *pEnumData; | |
| PFN_SELECTED_STORE_CB pfnSelectedStoreCallback; | |
| void *pvArg; | |
| } CRYPTUI_SELECTSTORE_INFO_A, *PCRYPTUI_SELECTSTORE_INFO_A; | |
| typedef struct _CRYPTUI_SELECTSTORE_INFO_W | |
| { | |
| DWORD dwSize; | |
| HWND parent; | |
| DWORD dwFlags; | |
| LPWSTR pwszTitle; | |
| LPWSTR pwszText; | |
| CRYPTUI_ENUM_DATA *pEnumData; | |
| PFN_SELECTED_STORE_CB pfnSelectedStoreCallback; | |
| void *pvArg; | |
| } CRYPTUI_SELECTSTORE_INFO_W, *PCRYPTUI_SELECTSTORE_INFO_W; | |
| struct StoreInfo | |
| { | |
| enum { | |
| StoreHandle, | |
| SystemStore | |
| } type; | |
| union { | |
| HCERTSTORE store; | |
| LPWSTR name; | |
| } DUMMYUNIONNAME; | |
| }; | |
| static BOOL WINAPI enum_store_callback(const void *pvSystemStore, | |
| DWORD dwFlags, PCERT_SYSTEM_STORE_INFO pStoreInfo, void *pvReserved, | |
| void *pvArg) | |
| { | |
| HWND tree = GetDlgItem(pvArg, IDC_STORE_LIST); | |
| TVINSERTSTRUCTW tvis; | |
| LPCWSTR localizedName; | |
| BOOL ret = TRUE; | |
| tvis.hParent = NULL; | |
| tvis.hInsertAfter = TVI_LAST; | |
| tvis.u.item.mask = TVIF_TEXT; | |
| if ((localizedName = CryptFindLocalizedName(pvSystemStore))) | |
| { | |
| struct StoreInfo *storeInfo = HeapAlloc(GetProcessHeap(), 0, | |
| sizeof(struct StoreInfo)); | |
| if (storeInfo) | |
| { | |
| storeInfo->type = SystemStore; | |
| storeInfo->u.name = HeapAlloc(GetProcessHeap(), 0, | |
| (strlenW(pvSystemStore) + 1) * sizeof(WCHAR)); | |
| if (storeInfo->u.name) | |
| { | |
| tvis.u.item.mask |= TVIF_PARAM; | |
| tvis.u.item.lParam = (LPARAM)storeInfo; | |
| strcpyW(storeInfo->u.name, pvSystemStore); | |
| } | |
| else | |
| { | |
| HeapFree(GetProcessHeap(), 0, storeInfo); | |
| ret = FALSE; | |
| } | |
| } | |
| else | |
| ret = FALSE; | |
| tvis.u.item.pszText = (LPWSTR)localizedName; | |
| } | |
| else | |
| tvis.u.item.pszText = (LPWSTR)pvSystemStore; | |
| /* FIXME: need a folder icon for the store too */ | |
| if (ret) | |
| SendMessageW(tree, TVM_INSERTITEMW, 0, (LPARAM)&tvis); | |
| return ret; | |
| } | |
| static void enumerate_stores(HWND hwnd, CRYPTUI_ENUM_DATA *pEnumData) | |
| { | |
| DWORD i; | |
| HWND tree = GetDlgItem(hwnd, IDC_STORE_LIST); | |
| for (i = 0; i < pEnumData->cEnumArgs; i++) | |
| CertEnumSystemStore(pEnumData->rgEnumArgs[i].dwFlags, | |
| pEnumData->rgEnumArgs[i].pvSystemStoreLocationPara, | |
| hwnd, enum_store_callback); | |
| for (i = 0; i < pEnumData->cStores; i++) | |
| { | |
| DWORD size; | |
| if (CertGetStoreProperty(pEnumData->rghStore[i], | |
| CERT_STORE_LOCALIZED_NAME_PROP_ID, NULL, &size)) | |
| { | |
| LPWSTR name = HeapAlloc(GetProcessHeap(), 0, size); | |
| if (name) | |
| { | |
| if (CertGetStoreProperty(pEnumData->rghStore[i], | |
| CERT_STORE_LOCALIZED_NAME_PROP_ID, name, &size)) | |
| { | |
| struct StoreInfo *storeInfo = HeapAlloc(GetProcessHeap(), | |
| 0, sizeof(struct StoreInfo)); | |
| if (storeInfo) | |
| { | |
| TVINSERTSTRUCTW tvis; | |
| storeInfo->type = StoreHandle; | |
| storeInfo->u.store = pEnumData->rghStore[i]; | |
| tvis.hParent = NULL; | |
| tvis.hInsertAfter = TVI_LAST; | |
| tvis.u.item.mask = TVIF_TEXT | TVIF_PARAM; | |
| tvis.u.item.pszText = name; | |
| tvis.u.item.lParam = (LPARAM)storeInfo; | |
| SendMessageW(tree, TVM_INSERTITEMW, 0, (LPARAM)&tvis); | |
| } | |
| } | |
| HeapFree(GetProcessHeap(), 0, name); | |
| } | |
| } | |
| } | |
| } | |
| static void free_store_info(HWND tree) | |
| { | |
| HTREEITEM next = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_CHILD, | |
| 0); | |
| while (next) | |
| { | |
| TVITEMW item; | |
| memset(&item, 0, sizeof(item)); | |
| item.mask = TVIF_HANDLE | TVIF_PARAM; | |
| item.hItem = next; | |
| SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item); | |
| if (item.lParam) | |
| { | |
| struct StoreInfo *storeInfo = (struct StoreInfo *)item.lParam; | |
| if (storeInfo->type == SystemStore) | |
| HeapFree(GetProcessHeap(), 0, storeInfo->u.name); | |
| HeapFree(GetProcessHeap(), 0, storeInfo); | |
| } | |
| next = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_NEXT, | |
| (LPARAM)next); | |
| } | |
| } | |
| static HCERTSTORE selected_item_to_store(HWND tree, HTREEITEM hItem) | |
| { | |
| WCHAR buf[MAX_STRING_LEN]; | |
| TVITEMW item; | |
| HCERTSTORE store; | |
| memset(&item, 0, sizeof(item)); | |
| item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT; | |
| item.hItem = hItem; | |
| item.cchTextMax = sizeof(buf) / sizeof(buf[0]); | |
| item.pszText = buf; | |
| SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item); | |
| if (item.lParam) | |
| { | |
| struct StoreInfo *storeInfo = (struct StoreInfo *)item.lParam; | |
| if (storeInfo->type == StoreHandle) | |
| store = storeInfo->u.store; | |
| else | |
| store = CertOpenSystemStoreW(0, storeInfo->u.name); | |
| } | |
| else | |
| { | |
| /* It's implicitly a system store */ | |
| store = CertOpenSystemStoreW(0, buf); | |
| } | |
| return store; | |
| } | |
| struct SelectStoreInfo | |
| { | |
| PCRYPTUI_SELECTSTORE_INFO_W info; | |
| HCERTSTORE store; | |
| }; | |
| static LRESULT CALLBACK select_store_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| struct SelectStoreInfo *selectInfo; | |
| LRESULT ret = 0; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| selectInfo = (struct SelectStoreInfo *)lp; | |
| SetWindowLongPtrW(hwnd, DWLP_USER, lp); | |
| if (selectInfo->info->pwszTitle) | |
| SendMessageW(hwnd, WM_SETTEXT, 0, | |
| (LPARAM)selectInfo->info->pwszTitle); | |
| if (selectInfo->info->pwszText) | |
| SendMessageW(GetDlgItem(hwnd, IDC_STORE_TEXT), WM_SETTEXT, 0, | |
| (LPARAM)selectInfo->info->pwszText); | |
| if (!(selectInfo->info->dwFlags & CRYPTUI_ENABLE_SHOW_PHYSICAL_STORE)) | |
| ShowWindow(GetDlgItem(hwnd, IDC_SHOW_PHYSICAL_STORES), FALSE); | |
| enumerate_stores(hwnd, selectInfo->info->pEnumData); | |
| break; | |
| } | |
| case WM_COMMAND: | |
| switch (wp) | |
| { | |
| case IDOK: | |
| { | |
| HWND tree = GetDlgItem(hwnd, IDC_STORE_LIST); | |
| HTREEITEM selection = (HTREEITEM)SendMessageW(tree, | |
| TVM_GETNEXTITEM, TVGN_CARET, 0); | |
| selectInfo = (struct SelectStoreInfo *)GetWindowLongPtrW(hwnd, | |
| DWLP_USER); | |
| if (!selection) | |
| { | |
| WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN], *pTitle; | |
| if (selectInfo->info->pwszTitle) | |
| pTitle = selectInfo->info->pwszTitle; | |
| else | |
| { | |
| LoadStringW(hInstance, IDS_SELECT_STORE_TITLE, title, | |
| sizeof(title) / sizeof(title[0])); | |
| pTitle = title; | |
| } | |
| LoadStringW(hInstance, IDS_SELECT_STORE, error, | |
| sizeof(error) / sizeof(error[0])); | |
| MessageBoxW(hwnd, error, pTitle, MB_ICONEXCLAMATION | MB_OK); | |
| } | |
| else | |
| { | |
| HCERTSTORE store = selected_item_to_store(tree, selection); | |
| if (!selectInfo->info->pfnSelectedStoreCallback || | |
| selectInfo->info->pfnSelectedStoreCallback(store, hwnd, | |
| selectInfo->info->pvArg)) | |
| { | |
| selectInfo->store = store; | |
| free_store_info(tree); | |
| EndDialog(hwnd, IDOK); | |
| } | |
| else | |
| CertCloseStore(store, 0); | |
| } | |
| ret = TRUE; | |
| break; | |
| } | |
| case IDCANCEL: | |
| free_store_info(GetDlgItem(hwnd, IDC_STORE_LIST)); | |
| EndDialog(hwnd, IDCANCEL); | |
| ret = TRUE; | |
| break; | |
| } | |
| break; | |
| } | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * CryptUIDlgSelectStoreW (CRYPTUI.@) | |
| */ | |
| HCERTSTORE WINAPI CryptUIDlgSelectStoreW(PCRYPTUI_SELECTSTORE_INFO_W info) | |
| { | |
| struct SelectStoreInfo selectInfo = { info, NULL }; | |
| TRACE("(%p)\n", info); | |
| if (info->dwSize != sizeof(CRYPTUI_SELECTSTORE_INFO_W)) | |
| { | |
| WARN("unexpected size %d\n", info->dwSize); | |
| SetLastError(E_INVALIDARG); | |
| return NULL; | |
| } | |
| DialogBoxParamW(hInstance, MAKEINTRESOURCEW(IDD_SELECT_STORE), info->parent, | |
| select_store_dlg_proc, (LPARAM)&selectInfo); | |
| return selectInfo.store; | |
| } | |
| /*********************************************************************** | |
| * CryptUIDlgSelectStoreA (CRYPTUI.@) | |
| */ | |
| HCERTSTORE WINAPI CryptUIDlgSelectStoreA(PCRYPTUI_SELECTSTORE_INFO_A info) | |
| { | |
| CRYPTUI_SELECTSTORE_INFO_W infoW; | |
| HCERTSTORE ret; | |
| int len; | |
| TRACE("(%p)\n", info); | |
| if (info->dwSize != sizeof(CRYPTUI_SELECTSTORE_INFO_A)) | |
| { | |
| WARN("unexpected size %d\n", info->dwSize); | |
| SetLastError(E_INVALIDARG); | |
| return NULL; | |
| } | |
| memcpy(&infoW, &info, sizeof(info)); | |
| if (info->pszTitle) | |
| { | |
| len = MultiByteToWideChar(CP_ACP, 0, info->pszTitle, -1, NULL, 0); | |
| infoW.pwszTitle = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
| MultiByteToWideChar(CP_ACP, 0, info->pszTitle, -1, infoW.pwszTitle, | |
| len); | |
| } | |
| if (info->pszText) | |
| { | |
| len = MultiByteToWideChar(CP_ACP, 0, info->pszText, -1, NULL, 0); | |
| infoW.pwszText = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
| MultiByteToWideChar(CP_ACP, 0, info->pszText, -1, infoW.pwszText, len); | |
| } | |
| ret = CryptUIDlgSelectStoreW(&infoW); | |
| HeapFree(GetProcessHeap(), 0, infoW.pwszText); | |
| HeapFree(GetProcessHeap(), 0, infoW.pwszTitle); | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * CryptUIDlgViewCertificateA (CRYPTUI.@) | |
| */ | |
| BOOL WINAPI CryptUIDlgViewCertificateA( | |
| PCCRYPTUI_VIEWCERTIFICATE_STRUCTA pCertViewInfo, BOOL *pfPropertiesChanged) | |
| { | |
| CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo; | |
| LPWSTR title = NULL; | |
| BOOL ret; | |
| TRACE("(%p, %p)\n", pCertViewInfo, pfPropertiesChanged); | |
| memcpy(&viewInfo, pCertViewInfo, sizeof(viewInfo)); | |
| if (pCertViewInfo->szTitle) | |
| { | |
| int len = MultiByteToWideChar(CP_ACP, 0, pCertViewInfo->szTitle, -1, | |
| NULL, 0); | |
| title = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
| if (title) | |
| { | |
| MultiByteToWideChar(CP_ACP, 0, pCertViewInfo->szTitle, -1, title, | |
| len); | |
| viewInfo.szTitle = title; | |
| } | |
| else | |
| { | |
| ret = FALSE; | |
| goto error; | |
| } | |
| } | |
| if (pCertViewInfo->cPropSheetPages) | |
| { | |
| FIXME("ignoring additional prop sheet pages\n"); | |
| viewInfo.cPropSheetPages = 0; | |
| } | |
| ret = CryptUIDlgViewCertificateW(&viewInfo, pfPropertiesChanged); | |
| HeapFree(GetProcessHeap(), 0, title); | |
| error: | |
| return ret; | |
| } | |
| struct ReadStringStruct | |
| { | |
| LPCWSTR buf; | |
| LONG pos; | |
| LONG len; | |
| }; | |
| static DWORD CALLBACK read_text_callback(DWORD_PTR dwCookie, LPBYTE buf, | |
| LONG cb, LONG *pcb) | |
| { | |
| struct ReadStringStruct *string = (struct ReadStringStruct *)dwCookie; | |
| LONG cch = min(cb / sizeof(WCHAR), string->len - string->pos); | |
| TRACE("(%p, %p, %d, %p)\n", string, buf, cb, pcb); | |
| memmove(buf, string->buf + string->pos, cch * sizeof(WCHAR)); | |
| string->pos += cch; | |
| *pcb = cch * sizeof(WCHAR); | |
| return 0; | |
| } | |
| static void add_unformatted_text_to_control(HWND hwnd, LPCWSTR text, LONG len) | |
| { | |
| struct ReadStringStruct string; | |
| EDITSTREAM editstream; | |
| TRACE("(%p, %s)\n", hwnd, debugstr_wn(text, len)); | |
| string.buf = text; | |
| string.pos = 0; | |
| string.len = len; | |
| editstream.dwCookie = (DWORD_PTR)&string; | |
| editstream.dwError = 0; | |
| editstream.pfnCallback = read_text_callback; | |
| SendMessageW(hwnd, EM_STREAMIN, SF_TEXT | SFF_SELECTION | SF_UNICODE, | |
| (LPARAM)&editstream); | |
| } | |
| static void add_string_resource_to_control(HWND hwnd, int id) | |
| { | |
| LPWSTR str; | |
| LONG len; | |
| len = LoadStringW(hInstance, id, (LPWSTR)&str, 0); | |
| add_unformatted_text_to_control(hwnd, str, len); | |
| } | |
| static void add_text_with_paraformat_to_control(HWND hwnd, LPCWSTR text, | |
| LONG len, const PARAFORMAT2 *fmt) | |
| { | |
| add_unformatted_text_to_control(hwnd, text, len); | |
| SendMessageW(hwnd, EM_SETPARAFORMAT, 0, (LPARAM)fmt); | |
| } | |
| static void add_string_resource_with_paraformat_to_control(HWND hwnd, int id, | |
| const PARAFORMAT2 *fmt) | |
| { | |
| LPWSTR str; | |
| LONG len; | |
| len = LoadStringW(hInstance, id, (LPWSTR)&str, 0); | |
| add_text_with_paraformat_to_control(hwnd, str, len, fmt); | |
| } | |
| static LPWSTR get_cert_name_string(PCCERT_CONTEXT pCertContext, DWORD dwType, | |
| DWORD dwFlags) | |
| { | |
| LPWSTR buf = NULL; | |
| DWORD len; | |
| len = CertGetNameStringW(pCertContext, dwType, dwFlags, NULL, NULL, 0); | |
| if (len) | |
| { | |
| buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
| if (buf) | |
| CertGetNameStringW(pCertContext, dwType, dwFlags, NULL, buf, len); | |
| } | |
| return buf; | |
| } | |
| static void add_cert_string_to_control(HWND hwnd, PCCERT_CONTEXT pCertContext, | |
| DWORD dwType, DWORD dwFlags) | |
| { | |
| LPWSTR name = get_cert_name_string(pCertContext, dwType, dwFlags); | |
| if (name) | |
| { | |
| /* Don't include NULL-terminator in output */ | |
| DWORD len = lstrlenW(name); | |
| add_unformatted_text_to_control(hwnd, name, len); | |
| HeapFree(GetProcessHeap(), 0, name); | |
| } | |
| } | |
| static void add_icon_to_control(HWND hwnd, int id) | |
| { | |
| HRESULT hr; | |
| LPRICHEDITOLE richEditOle = NULL; | |
| LPOLEOBJECT object = NULL; | |
| CLSID clsid; | |
| LPOLECACHE oleCache = NULL; | |
| FORMATETC formatEtc; | |
| DWORD conn; | |
| LPDATAOBJECT dataObject = NULL; | |
| HBITMAP bitmap = NULL; | |
| RECT rect; | |
| STGMEDIUM stgm; | |
| LPOLECLIENTSITE clientSite = NULL; | |
| REOBJECT reObject; | |
| TRACE("(%p, %d)\n", hwnd, id); | |
| SendMessageW(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&richEditOle); | |
| if (!richEditOle) | |
| goto end; | |
| hr = OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject, | |
| (void**)&object); | |
| if (FAILED(hr)) | |
| goto end; | |
| hr = IOleObject_GetUserClassID(object, &clsid); | |
| if (FAILED(hr)) | |
| goto end; | |
| hr = IOleObject_QueryInterface(object, &IID_IOleCache, (void**)&oleCache); | |
| if (FAILED(hr)) | |
| goto end; | |
| formatEtc.cfFormat = CF_BITMAP; | |
| formatEtc.ptd = NULL; | |
| formatEtc.dwAspect = DVASPECT_CONTENT; | |
| formatEtc.lindex = -1; | |
| formatEtc.tymed = TYMED_GDI; | |
| hr = IOleCache_Cache(oleCache, &formatEtc, 0, &conn); | |
| if (FAILED(hr)) | |
| goto end; | |
| hr = IOleObject_QueryInterface(object, &IID_IDataObject, | |
| (void**)&dataObject); | |
| if (FAILED(hr)) | |
| goto end; | |
| hr = IRichEditOle_GetClientSite(richEditOle, &clientSite); | |
| if (FAILED(hr)) | |
| goto end; | |
| bitmap = LoadImageW(hInstance, MAKEINTRESOURCEW(id), IMAGE_BITMAP, 0, 0, | |
| LR_DEFAULTSIZE | LR_LOADTRANSPARENT); | |
| if (!bitmap) | |
| goto end; | |
| rect.left = rect.top = 0; | |
| rect.right = GetSystemMetrics(SM_CXICON); | |
| rect.bottom = GetSystemMetrics(SM_CYICON); | |
| stgm.tymed = TYMED_GDI; | |
| stgm.u.hBitmap = bitmap; | |
| stgm.pUnkForRelease = NULL; | |
| hr = IDataObject_SetData(dataObject, &formatEtc, &stgm, TRUE); | |
| if (FAILED(hr)) | |
| goto end; | |
| reObject.cbStruct = sizeof(reObject); | |
| reObject.cp = REO_CP_SELECTION; | |
| reObject.clsid = clsid; | |
| reObject.poleobj = object; | |
| reObject.pstg = NULL; | |
| reObject.polesite = clientSite; | |
| reObject.sizel.cx = reObject.sizel.cy = 0; | |
| reObject.dvaspect = DVASPECT_CONTENT; | |
| reObject.dwFlags = 0; | |
| reObject.dwUser = 0; | |
| IRichEditOle_InsertObject(richEditOle, &reObject); | |
| end: | |
| if (clientSite) | |
| IOleClientSite_Release(clientSite); | |
| if (dataObject) | |
| IDataObject_Release(dataObject); | |
| if (oleCache) | |
| IOleCache_Release(oleCache); | |
| if (object) | |
| IOleObject_Release(object); | |
| if (richEditOle) | |
| IRichEditOle_Release(richEditOle); | |
| } | |
| #define MY_INDENT 200 | |
| static void add_oid_text_to_control(HWND hwnd, char *oid) | |
| { | |
| WCHAR nl = '\n'; | |
| PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, oid, 0); | |
| PARAFORMAT2 parFmt; | |
| parFmt.cbSize = sizeof(parFmt); | |
| parFmt.dwMask = PFM_STARTINDENT; | |
| parFmt.dxStartIndent = MY_INDENT * 3; | |
| if (oidInfo) | |
| { | |
| add_text_with_paraformat_to_control(hwnd, oidInfo->pwszName, | |
| lstrlenW(oidInfo->pwszName), &parFmt); | |
| add_unformatted_text_to_control(hwnd, &nl, 1); | |
| } | |
| } | |
| struct OIDToString | |
| { | |
| LPCSTR oid; | |
| int id; | |
| }; | |
| /* The following list MUST be lexicographically sorted by OID */ | |
| static struct OIDToString oidMap[] = { | |
| /* 1.3.6.1.4.1.311.10.3.1 */ | |
| { szOID_KP_CTL_USAGE_SIGNING, IDS_PURPOSE_CTL_USAGE_SIGNING }, | |
| /* 1.3.6.1.4.1.311.10.3.4 */ | |
| { szOID_KP_EFS, IDS_PURPOSE_EFS }, | |
| /* 1.3.6.1.4.1.311.10.3.4.1 */ | |
| { szOID_EFS_RECOVERY, IDS_PURPOSE_EFS_RECOVERY }, | |
| /* 1.3.6.1.4.1.311.10.3.5 */ | |
| { szOID_WHQL_CRYPTO, IDS_PURPOSE_WHQL }, | |
| /* 1.3.6.1.4.1.311.10.3.6 */ | |
| { szOID_NT5_CRYPTO, IDS_PURPOSE_NT5 }, | |
| /* 1.3.6.1.4.1.311.10.3.7 */ | |
| { szOID_OEM_WHQL_CRYPTO, IDS_PURPOSE_OEM_WHQL }, | |
| /* 1.3.6.1.4.1.311.10.3.8 */ | |
| { szOID_EMBEDDED_NT_CRYPTO, IDS_PURPOSE_EMBEDDED_NT }, | |
| /* 1.3.6.1.4.1.311.10.3.9 */ | |
| { szOID_ROOT_LIST_SIGNER, IDS_PURPOSE_ROOT_LIST_SIGNER }, | |
| /* 1.3.6.1.4.1.311.10.3.10 */ | |
| { szOID_KP_QUALIFIED_SUBORDINATION, IDS_PURPOSE_QUALIFIED_SUBORDINATION }, | |
| /* 1.3.6.1.4.1.311.10.3.11 */ | |
| { szOID_KP_KEY_RECOVERY, IDS_PURPOSE_KEY_RECOVERY }, | |
| /* 1.3.6.1.4.1.311.10.3.12 */ | |
| { szOID_KP_DOCUMENT_SIGNING, IDS_PURPOSE_DOCUMENT_SIGNING }, | |
| /* 1.3.6.1.4.1.311.10.3.13 */ | |
| { szOID_KP_LIFETIME_SIGNING, IDS_PURPOSE_LIFETIME_SIGNING }, | |
| /* 1.3.6.1.4.1.311.10.5.1 */ | |
| { szOID_DRM, IDS_PURPOSE_DRM }, | |
| /* 1.3.6.1.4.1.311.10.6.1 */ | |
| { szOID_LICENSES, IDS_PURPOSE_LICENSES }, | |
| /* 1.3.6.1.4.1.311.10.6.2 */ | |
| { szOID_LICENSE_SERVER, IDS_PURPOSE_LICENSE_SERVER }, | |
| /* 1.3.6.1.4.1.311.20.2.1 */ | |
| { szOID_ENROLLMENT_AGENT, IDS_PURPOSE_ENROLLMENT_AGENT }, | |
| /* 1.3.6.1.4.1.311.20.2.2 */ | |
| { szOID_KP_SMARTCARD_LOGON, IDS_PURPOSE_SMARTCARD_LOGON }, | |
| /* 1.3.6.1.4.1.311.21.5 */ | |
| { szOID_KP_CA_EXCHANGE, IDS_PURPOSE_CA_EXCHANGE }, | |
| /* 1.3.6.1.4.1.311.21.6 */ | |
| { szOID_KP_KEY_RECOVERY_AGENT, IDS_PURPOSE_KEY_RECOVERY_AGENT }, | |
| /* 1.3.6.1.4.1.311.21.19 */ | |
| { szOID_DS_EMAIL_REPLICATION, IDS_PURPOSE_DS_EMAIL_REPLICATION }, | |
| /* 1.3.6.1.5.5.7.3.1 */ | |
| { szOID_PKIX_KP_SERVER_AUTH, IDS_PURPOSE_SERVER_AUTH }, | |
| /* 1.3.6.1.5.5.7.3.2 */ | |
| { szOID_PKIX_KP_CLIENT_AUTH, IDS_PURPOSE_CLIENT_AUTH }, | |
| /* 1.3.6.1.5.5.7.3.3 */ | |
| { szOID_PKIX_KP_CODE_SIGNING, IDS_PURPOSE_CODE_SIGNING }, | |
| /* 1.3.6.1.5.5.7.3.4 */ | |
| { szOID_PKIX_KP_EMAIL_PROTECTION, IDS_PURPOSE_EMAIL_PROTECTION }, | |
| /* 1.3.6.1.5.5.7.3.5 */ | |
| { szOID_PKIX_KP_IPSEC_END_SYSTEM, IDS_PURPOSE_IPSEC }, | |
| /* 1.3.6.1.5.5.7.3.6 */ | |
| { szOID_PKIX_KP_IPSEC_TUNNEL, IDS_PURPOSE_IPSEC }, | |
| /* 1.3.6.1.5.5.7.3.7 */ | |
| { szOID_PKIX_KP_IPSEC_USER, IDS_PURPOSE_IPSEC }, | |
| /* 1.3.6.1.5.5.7.3.8 */ | |
| { szOID_PKIX_KP_TIMESTAMP_SIGNING, IDS_PURPOSE_TIMESTAMP_SIGNING }, | |
| }; | |
| static struct OIDToString *findSupportedOID(LPCSTR oid) | |
| { | |
| int indexHigh = sizeof(oidMap) / sizeof(oidMap[0]) - 1, indexLow = 0, i; | |
| struct OIDToString *ret = NULL; | |
| for (i = (indexLow + indexHigh) / 2; !ret && indexLow <= indexHigh; | |
| i = (indexLow + indexHigh) / 2) | |
| { | |
| int cmp; | |
| cmp = strcmp(oid, oidMap[i].oid); | |
| if (!cmp) | |
| ret = &oidMap[i]; | |
| else if (cmp > 0) | |
| indexLow = i + 1; | |
| else | |
| indexHigh = i - 1; | |
| } | |
| return ret; | |
| } | |
| static void add_local_oid_text_to_control(HWND text, LPCSTR oid) | |
| { | |
| struct OIDToString *entry; | |
| WCHAR nl = '\n'; | |
| PARAFORMAT2 parFmt; | |
| parFmt.cbSize = sizeof(parFmt); | |
| parFmt.dwMask = PFM_STARTINDENT; | |
| parFmt.dxStartIndent = MY_INDENT * 3; | |
| if ((entry = findSupportedOID(oid))) | |
| { | |
| WCHAR *str, *linebreak, *ptr; | |
| BOOL multiline = FALSE; | |
| int len; | |
| len = LoadStringW(hInstance, entry->id, (LPWSTR)&str, 0); | |
| ptr = str; | |
| do { | |
| if ((linebreak = memchrW(ptr, '\n', len))) | |
| { | |
| WCHAR copy[MAX_STRING_LEN]; | |
| multiline = TRUE; | |
| /* The source string contains a newline, which the richedit | |
| * control won't find since it's interpreted as a paragraph | |
| * break. Therefore copy up to the newline. lstrcpynW always | |
| * NULL-terminates, so pass one more than the length of the | |
| * source line so the copy includes the entire line and the | |
| * NULL-terminator. | |
| */ | |
| lstrcpynW(copy, ptr, linebreak - ptr + 1); | |
| add_text_with_paraformat_to_control(text, copy, | |
| linebreak - ptr, &parFmt); | |
| ptr = linebreak + 1; | |
| add_unformatted_text_to_control(text, &nl, 1); | |
| } | |
| else if (multiline && *ptr) | |
| { | |
| /* Add the last line */ | |
| add_text_with_paraformat_to_control(text, ptr, | |
| len - (ptr - str), &parFmt); | |
| add_unformatted_text_to_control(text, &nl, 1); | |
| } | |
| } while (linebreak); | |
| if (!multiline) | |
| { | |
| add_text_with_paraformat_to_control(text, str, len, &parFmt); | |
| add_unformatted_text_to_control(text, &nl, 1); | |
| } | |
| } | |
| else | |
| { | |
| WCHAR *oidW = HeapAlloc(GetProcessHeap(), 0, | |
| (strlen(oid) + 1) * sizeof(WCHAR)); | |
| if (oidW) | |
| { | |
| LPCSTR src; | |
| WCHAR *dst; | |
| for (src = oid, dst = oidW; *src; src++, dst++) | |
| *dst = *src; | |
| *dst = 0; | |
| add_text_with_paraformat_to_control(text, oidW, lstrlenW(oidW), | |
| &parFmt); | |
| add_unformatted_text_to_control(text, &nl, 1); | |
| HeapFree(GetProcessHeap(), 0, oidW); | |
| } | |
| } | |
| } | |
| static void display_app_usages(HWND text, PCCERT_CONTEXT cert, | |
| BOOL *anyUsageAdded) | |
| { | |
| static char any_app_policy[] = szOID_ANY_APPLICATION_POLICY; | |
| WCHAR nl = '\n'; | |
| CHARFORMATW charFmt; | |
| PCERT_EXTENSION policyExt; | |
| if (!*anyUsageAdded) | |
| { | |
| PARAFORMAT2 parFmt; | |
| parFmt.cbSize = sizeof(parFmt); | |
| parFmt.dwMask = PFM_STARTINDENT; | |
| parFmt.dxStartIndent = MY_INDENT; | |
| add_string_resource_with_paraformat_to_control(text, | |
| IDS_CERT_INFO_PURPOSES, &parFmt); | |
| add_unformatted_text_to_control(text, &nl, 1); | |
| *anyUsageAdded = TRUE; | |
| } | |
| memset(&charFmt, 0, sizeof(charFmt)); | |
| charFmt.cbSize = sizeof(charFmt); | |
| charFmt.dwMask = CFM_BOLD; | |
| charFmt.dwEffects = 0; | |
| SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt); | |
| if ((policyExt = CertFindExtension(szOID_APPLICATION_CERT_POLICIES, | |
| cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension))) | |
| { | |
| CERT_POLICIES_INFO *policies; | |
| DWORD size; | |
| if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT_POLICIES, | |
| policyExt->Value.pbData, policyExt->Value.cbData, | |
| CRYPT_DECODE_ALLOC_FLAG, NULL, &policies, &size)) | |
| { | |
| DWORD i; | |
| for (i = 0; i < policies->cPolicyInfo; i++) | |
| { | |
| DWORD j; | |
| for (j = 0; j < policies->rgPolicyInfo[i].cPolicyQualifier; j++) | |
| add_local_oid_text_to_control(text, | |
| policies->rgPolicyInfo[i].rgPolicyQualifier[j]. | |
| pszPolicyQualifierId); | |
| } | |
| LocalFree(policies); | |
| } | |
| } | |
| else | |
| add_oid_text_to_control(text, any_app_policy); | |
| } | |
| static BOOL display_cert_usages(HWND text, PCCERT_CONTEXT cert, | |
| BOOL *anyUsageAdded) | |
| { | |
| WCHAR nl = '\n'; | |
| DWORD size; | |
| BOOL badUsages = FALSE; | |
| if (CertGetEnhancedKeyUsage(cert, 0, NULL, &size)) | |
| { | |
| CHARFORMATW charFmt; | |
| static char any_cert_policy[] = szOID_ANY_CERT_POLICY; | |
| PCERT_ENHKEY_USAGE usage = HeapAlloc(GetProcessHeap(), 0, size); | |
| if (usage) | |
| { | |
| if (CertGetEnhancedKeyUsage(cert, 0, usage, &size)) | |
| { | |
| DWORD i; | |
| if (!*anyUsageAdded) | |
| { | |
| PARAFORMAT2 parFmt; | |
| parFmt.cbSize = sizeof(parFmt); | |
| parFmt.dwMask = PFM_STARTINDENT; | |
| parFmt.dxStartIndent = MY_INDENT; | |
| add_string_resource_with_paraformat_to_control(text, | |
| IDS_CERT_INFO_PURPOSES, &parFmt); | |
| add_unformatted_text_to_control(text, &nl, 1); | |
| *anyUsageAdded = TRUE; | |
| } | |
| memset(&charFmt, 0, sizeof(charFmt)); | |
| charFmt.cbSize = sizeof(charFmt); | |
| charFmt.dwMask = CFM_BOLD; | |
| charFmt.dwEffects = 0; | |
| SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, | |
| (LPARAM)&charFmt); | |
| if (!usage->cUsageIdentifier) | |
| add_oid_text_to_control(text, any_cert_policy); | |
| else | |
| for (i = 0; i < usage->cUsageIdentifier; i++) | |
| add_local_oid_text_to_control(text, | |
| usage->rgpszUsageIdentifier[i]); | |
| } | |
| else | |
| badUsages = TRUE; | |
| HeapFree(GetProcessHeap(), 0, usage); | |
| } | |
| else | |
| badUsages = TRUE; | |
| } | |
| else | |
| badUsages = TRUE; | |
| return badUsages; | |
| } | |
| static void set_policy_text(HWND text, | |
| PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo) | |
| { | |
| BOOL includeCertUsages = FALSE, includeAppUsages = FALSE; | |
| BOOL badUsages = FALSE, anyUsageAdded = FALSE; | |
| if (pCertViewInfo->cPurposes) | |
| { | |
| DWORD i; | |
| for (i = 0; i < pCertViewInfo->cPurposes; i++) | |
| { | |
| if (!strcmp(pCertViewInfo->rgszPurposes[i], szOID_ANY_CERT_POLICY)) | |
| includeCertUsages = TRUE; | |
| else if (!strcmp(pCertViewInfo->rgszPurposes[i], | |
| szOID_ANY_APPLICATION_POLICY)) | |
| includeAppUsages = TRUE; | |
| else | |
| badUsages = TRUE; | |
| } | |
| } | |
| else | |
| includeAppUsages = includeCertUsages = TRUE; | |
| if (includeAppUsages) | |
| display_app_usages(text, pCertViewInfo->pCertContext, &anyUsageAdded); | |
| if (includeCertUsages) | |
| badUsages = display_cert_usages(text, pCertViewInfo->pCertContext, | |
| &anyUsageAdded); | |
| if (badUsages) | |
| { | |
| PARAFORMAT2 parFmt; | |
| parFmt.cbSize = sizeof(parFmt); | |
| parFmt.dwMask = PFM_STARTINDENT; | |
| parFmt.dxStartIndent = MY_INDENT; | |
| add_string_resource_with_paraformat_to_control(text, | |
| IDS_CERT_INFO_BAD_PURPOSES, &parFmt); | |
| } | |
| } | |
| static CRYPT_OBJID_BLOB *find_policy_qualifier(CERT_POLICIES_INFO *policies, | |
| LPCSTR policyOid) | |
| { | |
| CRYPT_OBJID_BLOB *ret = NULL; | |
| DWORD i; | |
| for (i = 0; !ret && i < policies->cPolicyInfo; i++) | |
| { | |
| DWORD j; | |
| for (j = 0; !ret && j < policies->rgPolicyInfo[i].cPolicyQualifier; j++) | |
| if (!strcmp(policies->rgPolicyInfo[i].rgPolicyQualifier[j]. | |
| pszPolicyQualifierId, policyOid)) | |
| ret = &policies->rgPolicyInfo[i].rgPolicyQualifier[j]. | |
| Qualifier; | |
| } | |
| return ret; | |
| } | |
| static WCHAR *get_cps_str_from_qualifier(const CRYPT_OBJID_BLOB *qualifier) | |
| { | |
| LPWSTR qualifierStr = NULL; | |
| CERT_NAME_VALUE *qualifierValue; | |
| DWORD size; | |
| if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME_VALUE, | |
| qualifier->pbData, qualifier->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, | |
| &qualifierValue, &size)) | |
| { | |
| size = CertRDNValueToStrW(qualifierValue->dwValueType, | |
| &qualifierValue->Value, NULL, 0); | |
| qualifierStr = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); | |
| if (qualifierStr) | |
| CertRDNValueToStrW(qualifierValue->dwValueType, | |
| &qualifierValue->Value, qualifierStr, size); | |
| LocalFree(qualifierValue); | |
| } | |
| return qualifierStr; | |
| } | |
| static WCHAR *get_user_notice_from_qualifier(const CRYPT_OBJID_BLOB *qualifier) | |
| { | |
| LPWSTR str = NULL; | |
| CERT_POLICY_QUALIFIER_USER_NOTICE *qualifierValue; | |
| DWORD size; | |
| if (CryptDecodeObjectEx(X509_ASN_ENCODING, | |
| X509_PKIX_POLICY_QUALIFIER_USERNOTICE, | |
| qualifier->pbData, qualifier->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, | |
| &qualifierValue, &size)) | |
| { | |
| str = HeapAlloc(GetProcessHeap(), 0, | |
| (strlenW(qualifierValue->pszDisplayText) + 1) * sizeof(WCHAR)); | |
| if (str) | |
| strcpyW(str, qualifierValue->pszDisplayText); | |
| LocalFree(qualifierValue); | |
| } | |
| return str; | |
| } | |
| struct IssuerStatement | |
| { | |
| LPWSTR cps; | |
| LPWSTR userNotice; | |
| }; | |
| static void set_issuer_statement(HWND hwnd, | |
| PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo) | |
| { | |
| PCERT_EXTENSION policyExt; | |
| if (!(pCertViewInfo->dwFlags & CRYPTUI_DISABLE_ISSUERSTATEMENT) && | |
| (policyExt = CertFindExtension(szOID_CERT_POLICIES, | |
| pCertViewInfo->pCertContext->pCertInfo->cExtension, | |
| pCertViewInfo->pCertContext->pCertInfo->rgExtension))) | |
| { | |
| CERT_POLICIES_INFO *policies; | |
| DWORD size; | |
| if (CryptDecodeObjectEx(X509_ASN_ENCODING, policyExt->pszObjId, | |
| policyExt->Value.pbData, policyExt->Value.cbData, | |
| CRYPT_DECODE_ALLOC_FLAG, NULL, &policies, &size)) | |
| { | |
| CRYPT_OBJID_BLOB *qualifier; | |
| LPWSTR cps = NULL, userNotice = NULL; | |
| if ((qualifier = find_policy_qualifier(policies, | |
| szOID_PKIX_POLICY_QUALIFIER_CPS))) | |
| cps = get_cps_str_from_qualifier(qualifier); | |
| if ((qualifier = find_policy_qualifier(policies, | |
| szOID_PKIX_POLICY_QUALIFIER_USERNOTICE))) | |
| userNotice = get_user_notice_from_qualifier(qualifier); | |
| if (cps || userNotice) | |
| { | |
| struct IssuerStatement *issuerStatement = | |
| HeapAlloc(GetProcessHeap(), 0, sizeof(struct IssuerStatement)); | |
| if (issuerStatement) | |
| { | |
| issuerStatement->cps = cps; | |
| issuerStatement->userNotice = userNotice; | |
| EnableWindow(GetDlgItem(hwnd, IDC_ISSUERSTATEMENT), TRUE); | |
| SetWindowLongPtrW(hwnd, DWLP_USER, | |
| (ULONG_PTR)issuerStatement); | |
| } | |
| } | |
| LocalFree(policies); | |
| } | |
| } | |
| } | |
| static void set_cert_info(HWND hwnd, | |
| PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo) | |
| { | |
| CHARFORMATW charFmt; | |
| PARAFORMAT2 parFmt; | |
| HWND icon = GetDlgItem(hwnd, IDC_CERTIFICATE_ICON); | |
| HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_INFO); | |
| CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain( | |
| (CRYPT_PROVIDER_DATA *)pCertViewInfo->u.pCryptProviderData, | |
| pCertViewInfo->idxSigner, pCertViewInfo->fCounterSigner, | |
| pCertViewInfo->idxCounterSigner); | |
| CRYPT_PROVIDER_CERT *root = | |
| &provSigner->pasCertChain[provSigner->csCertChain - 1]; | |
| if (!provSigner->pChainContext || | |
| (provSigner->pChainContext->TrustStatus.dwErrorStatus & | |
| CERT_TRUST_IS_PARTIAL_CHAIN)) | |
| add_icon_to_control(icon, IDB_CERT_WARNING); | |
| else if (!root->fTrustedRoot) | |
| add_icon_to_control(icon, IDB_CERT_ERROR); | |
| else | |
| add_icon_to_control(icon, IDB_CERT); | |
| memset(&charFmt, 0, sizeof(charFmt)); | |
| charFmt.cbSize = sizeof(charFmt); | |
| charFmt.dwMask = CFM_BOLD; | |
| charFmt.dwEffects = CFE_BOLD; | |
| SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt); | |
| /* FIXME: vertically center text */ | |
| parFmt.cbSize = sizeof(parFmt); | |
| parFmt.dwMask = PFM_STARTINDENT; | |
| parFmt.dxStartIndent = MY_INDENT; | |
| add_string_resource_with_paraformat_to_control(text, | |
| IDS_CERTIFICATEINFORMATION, &parFmt); | |
| text = GetDlgItem(hwnd, IDC_CERTIFICATE_STATUS); | |
| SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt); | |
| if (provSigner->dwError == TRUST_E_CERT_SIGNATURE) | |
| add_string_resource_with_paraformat_to_control(text, | |
| IDS_CERT_INFO_BAD_SIG, &parFmt); | |
| else if (!provSigner->pChainContext || | |
| (provSigner->pChainContext->TrustStatus.dwErrorStatus & | |
| CERT_TRUST_IS_PARTIAL_CHAIN)) | |
| add_string_resource_with_paraformat_to_control(text, | |
| IDS_CERT_INFO_PARTIAL_CHAIN, &parFmt); | |
| else if (!root->fTrustedRoot) | |
| { | |
| if (provSigner->csCertChain == 1 && root->fSelfSigned) | |
| add_string_resource_with_paraformat_to_control(text, | |
| IDS_CERT_INFO_UNTRUSTED_CA, &parFmt); | |
| else | |
| add_string_resource_with_paraformat_to_control(text, | |
| IDS_CERT_INFO_UNTRUSTED_ROOT, &parFmt); | |
| } | |
| else | |
| { | |
| set_policy_text(text, pCertViewInfo); | |
| set_issuer_statement(hwnd, pCertViewInfo); | |
| } | |
| } | |
| static void set_cert_name_string(HWND hwnd, PCCERT_CONTEXT cert, | |
| DWORD nameFlags, int heading) | |
| { | |
| WCHAR nl = '\n'; | |
| HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_NAMES); | |
| CHARFORMATW charFmt; | |
| PARAFORMAT2 parFmt; | |
| memset(&charFmt, 0, sizeof(charFmt)); | |
| charFmt.cbSize = sizeof(charFmt); | |
| charFmt.dwMask = CFM_BOLD; | |
| charFmt.dwEffects = CFE_BOLD; | |
| SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt); | |
| parFmt.cbSize = sizeof(parFmt); | |
| parFmt.dwMask = PFM_STARTINDENT; | |
| parFmt.dxStartIndent = MY_INDENT * 3; | |
| add_string_resource_with_paraformat_to_control(text, heading, &parFmt); | |
| charFmt.dwEffects = 0; | |
| SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt); | |
| add_cert_string_to_control(text, cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, | |
| nameFlags); | |
| add_unformatted_text_to_control(text, &nl, 1); | |
| add_unformatted_text_to_control(text, &nl, 1); | |
| add_unformatted_text_to_control(text, &nl, 1); | |
| } | |
| static void add_date_string_to_control(HWND hwnd, const FILETIME *fileTime) | |
| { | |
| WCHAR dateFmt[80]; /* sufficient for all versions of LOCALE_SSHORTDATE */ | |
| WCHAR date[80]; | |
| SYSTEMTIME sysTime; | |
| GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SSHORTDATE, dateFmt, | |
| sizeof(dateFmt) / sizeof(dateFmt[0])); | |
| FileTimeToSystemTime(fileTime, &sysTime); | |
| GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, date, | |
| sizeof(date) / sizeof(date[0])); | |
| add_unformatted_text_to_control(hwnd, date, lstrlenW(date)); | |
| } | |
| static void set_cert_validity_period(HWND hwnd, PCCERT_CONTEXT cert) | |
| { | |
| WCHAR nl = '\n'; | |
| HWND text = GetDlgItem(hwnd, IDC_CERTIFICATE_NAMES); | |
| CHARFORMATW charFmt; | |
| PARAFORMAT2 parFmt; | |
| memset(&charFmt, 0, sizeof(charFmt)); | |
| charFmt.cbSize = sizeof(charFmt); | |
| charFmt.dwMask = CFM_BOLD; | |
| charFmt.dwEffects = CFE_BOLD; | |
| SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt); | |
| parFmt.cbSize = sizeof(parFmt); | |
| parFmt.dwMask = PFM_STARTINDENT; | |
| parFmt.dxStartIndent = MY_INDENT * 3; | |
| add_string_resource_with_paraformat_to_control(text, IDS_VALID_FROM, | |
| &parFmt); | |
| charFmt.dwEffects = 0; | |
| SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt); | |
| add_date_string_to_control(text, &cert->pCertInfo->NotBefore); | |
| charFmt.dwEffects = CFE_BOLD; | |
| SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt); | |
| add_string_resource_to_control(text, IDS_VALID_TO); | |
| charFmt.dwEffects = 0; | |
| SendMessageW(text, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&charFmt); | |
| add_date_string_to_control(text, &cert->pCertInfo->NotAfter); | |
| add_unformatted_text_to_control(text, &nl, 1); | |
| } | |
| static void set_general_info(HWND hwnd, | |
| PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo) | |
| { | |
| set_cert_info(hwnd, pCertViewInfo); | |
| set_cert_name_string(hwnd, pCertViewInfo->pCertContext, 0, | |
| IDS_SUBJECT_HEADING); | |
| set_cert_name_string(hwnd, pCertViewInfo->pCertContext, | |
| CERT_NAME_ISSUER_FLAG, IDS_ISSUER_HEADING); | |
| set_cert_validity_period(hwnd, pCertViewInfo->pCertContext); | |
| } | |
| static LRESULT CALLBACK user_notice_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| LRESULT ret = 0; | |
| HWND text; | |
| struct IssuerStatement *issuerStatement; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| text = GetDlgItem(hwnd, IDC_USERNOTICE); | |
| issuerStatement = (struct IssuerStatement *)lp; | |
| add_unformatted_text_to_control(text, issuerStatement->userNotice, | |
| strlenW(issuerStatement->userNotice)); | |
| if (issuerStatement->cps) | |
| SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)issuerStatement->cps); | |
| else | |
| EnableWindow(GetDlgItem(hwnd, IDC_CPS), FALSE); | |
| break; | |
| case WM_COMMAND: | |
| switch (wp) | |
| { | |
| case IDOK: | |
| EndDialog(hwnd, IDOK); | |
| ret = TRUE; | |
| break; | |
| case IDC_CPS: | |
| { | |
| IBindCtx *bctx = NULL; | |
| LPWSTR cps; | |
| CreateBindCtx(0, &bctx); | |
| cps = (LPWSTR)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| HlinkSimpleNavigateToString(cps, NULL, NULL, NULL, bctx, NULL, | |
| HLNF_OPENINNEWWINDOW, 0); | |
| IBindCtx_Release(bctx); | |
| break; | |
| } | |
| } | |
| } | |
| return ret; | |
| } | |
| static void show_user_notice(HWND hwnd, struct IssuerStatement *issuerStatement) | |
| { | |
| DialogBoxParamW(hInstance, MAKEINTRESOURCEW(IDD_USERNOTICE), hwnd, | |
| user_notice_dlg_proc, (LPARAM)issuerStatement); | |
| } | |
| static LRESULT CALLBACK general_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| PROPSHEETPAGEW *page; | |
| PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo; | |
| TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp); | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| page = (PROPSHEETPAGEW *)lp; | |
| pCertViewInfo = (PCCRYPTUI_VIEWCERTIFICATE_STRUCTW)page->lParam; | |
| if (pCertViewInfo->dwFlags & CRYPTUI_DISABLE_ADDTOSTORE) | |
| ShowWindow(GetDlgItem(hwnd, IDC_ADDTOSTORE), FALSE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_ISSUERSTATEMENT), FALSE); | |
| set_general_info(hwnd, pCertViewInfo); | |
| break; | |
| case WM_COMMAND: | |
| switch (wp) | |
| { | |
| case IDC_ADDTOSTORE: | |
| CryptUIWizImport(0, hwnd, NULL, NULL, NULL); | |
| break; | |
| case IDC_ISSUERSTATEMENT: | |
| { | |
| struct IssuerStatement *issuerStatement = | |
| (struct IssuerStatement *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if (issuerStatement) | |
| { | |
| if (issuerStatement->userNotice) | |
| show_user_notice(hwnd, issuerStatement); | |
| else if (issuerStatement->cps) | |
| { | |
| IBindCtx *bctx = NULL; | |
| CreateBindCtx(0, &bctx); | |
| HlinkSimpleNavigateToString(issuerStatement->cps, NULL, | |
| NULL, NULL, bctx, NULL, HLNF_OPENINNEWWINDOW, 0); | |
| IBindCtx_Release(bctx); | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| return 0; | |
| } | |
| static UINT CALLBACK general_callback_proc(HWND hwnd, UINT msg, | |
| PROPSHEETPAGEW *page) | |
| { | |
| struct IssuerStatement *issuerStatement; | |
| switch (msg) | |
| { | |
| case PSPCB_RELEASE: | |
| issuerStatement = | |
| (struct IssuerStatement *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if (issuerStatement) | |
| { | |
| HeapFree(GetProcessHeap(), 0, issuerStatement->cps); | |
| HeapFree(GetProcessHeap(), 0, issuerStatement->userNotice); | |
| HeapFree(GetProcessHeap(), 0, issuerStatement); | |
| } | |
| break; | |
| } | |
| return 1; | |
| } | |
| static void init_general_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo, | |
| PROPSHEETPAGEW *page) | |
| { | |
| memset(page, 0, sizeof(PROPSHEETPAGEW)); | |
| page->dwSize = sizeof(PROPSHEETPAGEW); | |
| page->dwFlags = PSP_USECALLBACK; | |
| page->pfnCallback = general_callback_proc; | |
| page->hInstance = hInstance; | |
| page->u.pszTemplate = MAKEINTRESOURCEW(IDD_GENERAL); | |
| page->pfnDlgProc = general_dlg_proc; | |
| page->lParam = (LPARAM)pCertViewInfo; | |
| } | |
| typedef WCHAR * (*field_format_func)(PCCERT_CONTEXT cert); | |
| static WCHAR *field_format_version(PCCERT_CONTEXT cert) | |
| { | |
| static const WCHAR fmt[] = { 'V','%','d',0 }; | |
| WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, 12 * sizeof(WCHAR)); | |
| if (buf) | |
| sprintfW(buf, fmt, cert->pCertInfo->dwVersion); | |
| return buf; | |
| } | |
| static WCHAR *format_hex_string(void *pb, DWORD cb) | |
| { | |
| WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, (cb * 3 + 1) * sizeof(WCHAR)); | |
| if (buf) | |
| { | |
| static const WCHAR fmt[] = { '%','0','2','x',' ',0 }; | |
| DWORD i; | |
| WCHAR *ptr; | |
| for (i = 0, ptr = buf; i < cb; i++, ptr += 3) | |
| sprintfW(ptr, fmt, ((BYTE *)pb)[i]); | |
| } | |
| return buf; | |
| } | |
| static WCHAR *field_format_serial_number(PCCERT_CONTEXT cert) | |
| { | |
| return format_hex_string(cert->pCertInfo->SerialNumber.pbData, | |
| cert->pCertInfo->SerialNumber.cbData); | |
| } | |
| static WCHAR *field_format_issuer(PCCERT_CONTEXT cert) | |
| { | |
| return get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, | |
| CERT_NAME_ISSUER_FLAG); | |
| } | |
| static WCHAR *field_format_detailed_cert_name(PCERT_NAME_BLOB name) | |
| { | |
| WCHAR *str = NULL; | |
| DWORD len = CertNameToStrW(X509_ASN_ENCODING, name, | |
| CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG, NULL, 0); | |
| if (len) | |
| { | |
| str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
| if (str) | |
| CertNameToStrW(X509_ASN_ENCODING, name, | |
| CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG, str, len); | |
| } | |
| return str; | |
| } | |
| static WCHAR *field_format_detailed_issuer(PCCERT_CONTEXT cert, void *param) | |
| { | |
| return field_format_detailed_cert_name(&cert->pCertInfo->Issuer); | |
| } | |
| static WCHAR *field_format_subject(PCCERT_CONTEXT cert) | |
| { | |
| return get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0); | |
| } | |
| static WCHAR *field_format_detailed_subject(PCCERT_CONTEXT cert, void *param) | |
| { | |
| return field_format_detailed_cert_name(&cert->pCertInfo->Subject); | |
| } | |
| static WCHAR *format_long_date(const FILETIME *fileTime) | |
| { | |
| WCHAR dateFmt[80]; /* long enough for LOCALE_SLONGDATE */ | |
| DWORD len; | |
| WCHAR *buf = NULL; | |
| SYSTEMTIME sysTime; | |
| /* FIXME: format isn't quite right, want time too */ | |
| GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SLONGDATE, dateFmt, | |
| sizeof(dateFmt) / sizeof(dateFmt[0])); | |
| FileTimeToSystemTime(fileTime, &sysTime); | |
| len = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, NULL, 0); | |
| if (len) | |
| { | |
| buf = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
| if (buf) | |
| GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, buf, | |
| len); | |
| } | |
| return buf; | |
| } | |
| static WCHAR *field_format_from_date(PCCERT_CONTEXT cert) | |
| { | |
| return format_long_date(&cert->pCertInfo->NotBefore); | |
| } | |
| static WCHAR *field_format_to_date(PCCERT_CONTEXT cert) | |
| { | |
| return format_long_date(&cert->pCertInfo->NotAfter); | |
| } | |
| static WCHAR *field_format_public_key(PCCERT_CONTEXT cert) | |
| { | |
| PCCRYPT_OID_INFO oidInfo; | |
| WCHAR *buf = NULL; | |
| oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, | |
| cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, 0); | |
| if (oidInfo) | |
| { | |
| WCHAR fmt[MAX_STRING_LEN]; | |
| if (LoadStringW(hInstance, IDS_FIELD_PUBLIC_KEY_FORMAT, fmt, | |
| sizeof(fmt) / sizeof(fmt[0]))) | |
| { | |
| /* Allocate the output buffer. Use the number of bytes in the | |
| * public key as a conservative (high) estimate for the number of | |
| * digits in its output. | |
| * The output is of the form (in English) | |
| * "<public key algorithm> (<public key bit length> bits)". | |
| * Ordinarily having two positional parameters in a string is not a | |
| * good idea, but as this isn't a sentence fragment, it shouldn't | |
| * be word-order dependent. | |
| */ | |
| buf = HeapAlloc(GetProcessHeap(), 0, | |
| (strlenW(fmt) + strlenW(oidInfo->pwszName) + | |
| cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData * 8) | |
| * sizeof(WCHAR)); | |
| if (buf) | |
| sprintfW(buf, fmt, oidInfo->pwszName, | |
| CertGetPublicKeyLength(X509_ASN_ENCODING, | |
| &cert->pCertInfo->SubjectPublicKeyInfo)); | |
| } | |
| } | |
| return buf; | |
| } | |
| static WCHAR *field_format_detailed_public_key(PCCERT_CONTEXT cert, void *param) | |
| { | |
| return format_hex_string( | |
| cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData, | |
| cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData); | |
| } | |
| struct field_value_data; | |
| struct detail_data | |
| { | |
| PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo; | |
| BOOL *pfPropertiesChanged; | |
| int cFields; | |
| struct field_value_data *fields; | |
| }; | |
| typedef void (*add_fields_func)(HWND hwnd, struct detail_data *data); | |
| typedef WCHAR *(*create_detailed_value_func)(PCCERT_CONTEXT cert, void *param); | |
| struct field_value_data | |
| { | |
| create_detailed_value_func create; | |
| LPWSTR detailed_value; | |
| void *param; | |
| }; | |
| static void add_field_value_data(struct detail_data *data, | |
| create_detailed_value_func create, void *param) | |
| { | |
| if (data->cFields) | |
| data->fields = HeapReAlloc(GetProcessHeap(), 0, data->fields, | |
| (data->cFields + 1) * sizeof(struct field_value_data)); | |
| else | |
| data->fields = HeapAlloc(GetProcessHeap(), 0, | |
| sizeof(struct field_value_data)); | |
| if (data->fields) | |
| { | |
| data->fields[data->cFields].create = create; | |
| data->fields[data->cFields].detailed_value = NULL; | |
| data->fields[data->cFields].param = param; | |
| data->cFields++; | |
| } | |
| } | |
| static void add_field_and_value_to_list(HWND hwnd, struct detail_data *data, | |
| LPWSTR field, LPWSTR value, create_detailed_value_func create, void *param) | |
| { | |
| LVITEMW item; | |
| int iItem = SendMessageW(hwnd, LVM_GETITEMCOUNT, 0, 0); | |
| item.mask = LVIF_TEXT | LVIF_PARAM; | |
| item.iItem = iItem; | |
| item.iSubItem = 0; | |
| item.pszText = field; | |
| item.lParam = (LPARAM)data; | |
| SendMessageW(hwnd, LVM_INSERTITEMW, 0, (LPARAM)&item); | |
| if (value) | |
| { | |
| item.pszText = value; | |
| item.iSubItem = 1; | |
| SendMessageW(hwnd, LVM_SETITEMTEXTW, iItem, (LPARAM)&item); | |
| } | |
| add_field_value_data(data, create, param); | |
| } | |
| static void add_string_id_and_value_to_list(HWND hwnd, struct detail_data *data, | |
| int id, LPWSTR value, create_detailed_value_func create, void *param) | |
| { | |
| WCHAR buf[MAX_STRING_LEN]; | |
| LoadStringW(hInstance, id, buf, sizeof(buf) / sizeof(buf[0])); | |
| add_field_and_value_to_list(hwnd, data, buf, value, create, param); | |
| } | |
| struct v1_field | |
| { | |
| int id; | |
| field_format_func format; | |
| create_detailed_value_func create_detailed_value; | |
| }; | |
| static void add_v1_field(HWND hwnd, struct detail_data *data, | |
| const struct v1_field *field) | |
| { | |
| WCHAR *val = field->format(data->pCertViewInfo->pCertContext); | |
| if (val) | |
| { | |
| add_string_id_and_value_to_list(hwnd, data, field->id, val, | |
| field->create_detailed_value, NULL); | |
| HeapFree(GetProcessHeap(), 0, val); | |
| } | |
| } | |
| static const struct v1_field v1_fields[] = { | |
| { IDS_FIELD_VERSION, field_format_version, NULL }, | |
| { IDS_FIELD_SERIAL_NUMBER, field_format_serial_number, NULL }, | |
| { IDS_FIELD_ISSUER, field_format_issuer, field_format_detailed_issuer }, | |
| { IDS_FIELD_VALID_FROM, field_format_from_date, NULL }, | |
| { IDS_FIELD_VALID_TO, field_format_to_date, NULL }, | |
| { IDS_FIELD_SUBJECT, field_format_subject, field_format_detailed_subject }, | |
| { IDS_FIELD_PUBLIC_KEY, field_format_public_key, | |
| field_format_detailed_public_key } | |
| }; | |
| static void add_v1_fields(HWND hwnd, struct detail_data *data) | |
| { | |
| int i; | |
| PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext; | |
| /* The last item in v1_fields is the public key, which is not in the loop | |
| * because it's a special case. | |
| */ | |
| for (i = 0; i < sizeof(v1_fields) / sizeof(v1_fields[0]) - 1; i++) | |
| add_v1_field(hwnd, data, &v1_fields[i]); | |
| if (cert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData) | |
| add_v1_field(hwnd, data, &v1_fields[i]); | |
| } | |
| static WCHAR *crypt_format_extension(const CERT_EXTENSION *ext, DWORD formatStrType) | |
| { | |
| WCHAR *str = NULL; | |
| DWORD size; | |
| if (CryptFormatObject(X509_ASN_ENCODING, 0, formatStrType, NULL, | |
| ext->pszObjId, ext->Value.pbData, ext->Value.cbData, NULL, &size)) | |
| { | |
| str = HeapAlloc(GetProcessHeap(), 0, size); | |
| CryptFormatObject(X509_ASN_ENCODING, 0, formatStrType, NULL, | |
| ext->pszObjId, ext->Value.pbData, ext->Value.cbData, str, &size); | |
| } | |
| return str; | |
| } | |
| static WCHAR *field_format_extension_hex_with_ascii(const CERT_EXTENSION *ext) | |
| { | |
| WCHAR *str = NULL; | |
| if (ext->Value.cbData) | |
| { | |
| /* The output is formatted as: | |
| * <hex bytes> <ascii bytes>\n | |
| * where <hex bytes> is a string of up to 8 bytes, output as %02x, | |
| * and <ascii bytes> is the ASCII equivalent of each byte, or '.' if | |
| * the byte is not printable. | |
| * So, for example, the extension value consisting of the following | |
| * bytes: | |
| * 0x30,0x14,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x03, | |
| * 0x13,0x09,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e,0x67 | |
| * is output as: | |
| * 30 14 31 12 30 10 06 03 0.1.0... | |
| * 55 04 03 13 09 4a 75 61 U....Jua | |
| * 6e 20 4c 61 6e 67 n Lang | |
| * The allocation size therefore requires: | |
| * - 4 characters per character in an 8-byte line | |
| * (2 for the hex format, one for the space, one for the ASCII value) | |
| * - 3 more characters per 8-byte line (two spaces and a newline) | |
| * - 1 character for the terminating nul | |
| * FIXME: should use a fixed-width font for this | |
| */ | |
| DWORD lines = (ext->Value.cbData + 7) / 8; | |
| str = HeapAlloc(GetProcessHeap(), 0, | |
| (lines * 8 * 4 + lines * 3 + 1) * sizeof(WCHAR)); | |
| if (str) | |
| { | |
| static const WCHAR fmt[] = { '%','0','2','x',' ',0 }; | |
| DWORD i, j; | |
| WCHAR *ptr; | |
| for (i = 0, ptr = str; i < ext->Value.cbData; i += 8) | |
| { | |
| /* Output as hex bytes first */ | |
| for (j = i; j < min(i + 8, ext->Value.cbData); j++, ptr += 3) | |
| sprintfW(ptr, fmt, ext->Value.pbData[j]); | |
| /* Pad the hex output with spaces for alignment */ | |
| if (j == ext->Value.cbData && j % 8) | |
| { | |
| static const WCHAR pad[] = { ' ',' ',' ' }; | |
| for (; j % 8; j++, ptr += sizeof(pad) / sizeof(pad[0])) | |
| memcpy(ptr, pad, sizeof(pad)); | |
| } | |
| /* The last sprintfW included a space, so just insert one | |
| * more space between the hex bytes and the ASCII output | |
| */ | |
| *ptr++ = ' '; | |
| /* Output as ASCII bytes */ | |
| for (j = i; j < min(i + 8, ext->Value.cbData); j++, ptr++) | |
| { | |
| if (isprintW(ext->Value.pbData[j]) && | |
| !isspaceW(ext->Value.pbData[j])) | |
| *ptr = ext->Value.pbData[j]; | |
| else | |
| *ptr = '.'; | |
| } | |
| *ptr++ = '\n'; | |
| } | |
| *ptr++ = '\0'; | |
| } | |
| } | |
| return str; | |
| } | |
| static WCHAR *field_format_detailed_extension(PCCERT_CONTEXT cert, void *param) | |
| { | |
| PCERT_EXTENSION ext = param; | |
| LPWSTR str = crypt_format_extension(ext, | |
| CRYPT_FORMAT_STR_MULTI_LINE | CRYPT_FORMAT_STR_NO_HEX); | |
| if (!str) | |
| str = field_format_extension_hex_with_ascii(ext); | |
| return str; | |
| } | |
| static void add_cert_extension_detail(HWND hwnd, struct detail_data *data, | |
| PCERT_EXTENSION ext) | |
| { | |
| PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, | |
| ext->pszObjId, 0); | |
| LPWSTR val = crypt_format_extension(ext, 0); | |
| if (oidInfo) | |
| add_field_and_value_to_list(hwnd, data, (LPWSTR)oidInfo->pwszName, | |
| val, field_format_detailed_extension, ext); | |
| else | |
| { | |
| DWORD len = strlen(ext->pszObjId); | |
| LPWSTR oidW = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR)); | |
| if (oidW) | |
| { | |
| DWORD i; | |
| for (i = 0; i <= len; i++) | |
| oidW[i] = ext->pszObjId[i]; | |
| add_field_and_value_to_list(hwnd, data, oidW, val, | |
| field_format_detailed_extension, ext); | |
| HeapFree(GetProcessHeap(), 0, oidW); | |
| } | |
| } | |
| HeapFree(GetProcessHeap(), 0, val); | |
| } | |
| static void add_all_extensions(HWND hwnd, struct detail_data *data) | |
| { | |
| DWORD i; | |
| PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext; | |
| for (i = 0; i < cert->pCertInfo->cExtension; i++) | |
| add_cert_extension_detail(hwnd, data, &cert->pCertInfo->rgExtension[i]); | |
| } | |
| static void add_critical_extensions(HWND hwnd, struct detail_data *data) | |
| { | |
| DWORD i; | |
| PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext; | |
| for (i = 0; i < cert->pCertInfo->cExtension; i++) | |
| if (cert->pCertInfo->rgExtension[i].fCritical) | |
| add_cert_extension_detail(hwnd, data, | |
| &cert->pCertInfo->rgExtension[i]); | |
| } | |
| typedef WCHAR * (*prop_to_value_func)(void *pb, DWORD cb); | |
| struct prop_id_to_string_id | |
| { | |
| DWORD prop; | |
| int id; | |
| BOOL prop_is_string; | |
| prop_to_value_func prop_to_value; | |
| }; | |
| static WCHAR *format_enhanced_key_usage_value(void *pb, DWORD cb) | |
| { | |
| CERT_EXTENSION ext; | |
| ext.pszObjId = (LPSTR)X509_ENHANCED_KEY_USAGE; | |
| ext.fCritical = FALSE; | |
| ext.Value.pbData = pb; | |
| ext.Value.cbData = cb; | |
| return crypt_format_extension(&ext, 0); | |
| } | |
| /* Logically the access state should also be checked, and IDC_EDITPROPERTIES | |
| * disabled for read-only certificates, but native doesn't appear to do that. | |
| */ | |
| static const struct prop_id_to_string_id prop_id_map[] = { | |
| { CERT_HASH_PROP_ID, IDS_PROP_HASH, FALSE, format_hex_string }, | |
| { CERT_FRIENDLY_NAME_PROP_ID, IDS_PROP_FRIENDLY_NAME, TRUE, NULL }, | |
| { CERT_DESCRIPTION_PROP_ID, IDS_PROP_DESCRIPTION, TRUE, NULL }, | |
| { CERT_ENHKEY_USAGE_PROP_ID, IDS_PROP_ENHKEY_USAGE, FALSE, | |
| format_enhanced_key_usage_value }, | |
| }; | |
| static void add_properties(HWND hwnd, struct detail_data *data) | |
| { | |
| DWORD i; | |
| PCCERT_CONTEXT cert = data->pCertViewInfo->pCertContext; | |
| for (i = 0; i < sizeof(prop_id_map) / sizeof(prop_id_map[0]); i++) | |
| { | |
| DWORD cb; | |
| if (CertGetCertificateContextProperty(cert, prop_id_map[i].prop, NULL, | |
| &cb)) | |
| { | |
| BYTE *pb; | |
| WCHAR *val = NULL; | |
| /* FIXME: MS adds a separate value for the signature hash | |
| * algorithm. | |
| */ | |
| pb = HeapAlloc(GetProcessHeap(), 0, cb); | |
| if (pb) | |
| { | |
| if (CertGetCertificateContextProperty(cert, | |
| prop_id_map[i].prop, pb, &cb)) | |
| { | |
| if (prop_id_map[i].prop_is_string) | |
| { | |
| val = (LPWSTR)pb; | |
| /* Don't double-free pb */ | |
| pb = NULL; | |
| } | |
| else | |
| val = prop_id_map[i].prop_to_value(pb, cb); | |
| } | |
| HeapFree(GetProcessHeap(), 0, pb); | |
| } | |
| add_string_id_and_value_to_list(hwnd, data, prop_id_map[i].id, val, | |
| NULL, NULL); | |
| } | |
| } | |
| } | |
| static void add_all_fields(HWND hwnd, struct detail_data *data) | |
| { | |
| add_v1_fields(hwnd, data); | |
| add_all_extensions(hwnd, data); | |
| add_properties(hwnd, data); | |
| } | |
| struct selection_list_item | |
| { | |
| int id; | |
| add_fields_func add; | |
| }; | |
| const struct selection_list_item listItems[] = { | |
| { IDS_FIELDS_ALL, add_all_fields }, | |
| { IDS_FIELDS_V1, add_v1_fields }, | |
| { IDS_FIELDS_EXTENSIONS, add_all_extensions }, | |
| { IDS_FIELDS_CRITICAL_EXTENSIONS, add_critical_extensions }, | |
| { IDS_FIELDS_PROPERTIES, add_properties }, | |
| }; | |
| static void create_show_list(HWND hwnd, struct detail_data *data) | |
| { | |
| HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT); | |
| WCHAR buf[MAX_STRING_LEN]; | |
| int i; | |
| for (i = 0; i < sizeof(listItems) / sizeof(listItems[0]); i++) | |
| { | |
| int index; | |
| LoadStringW(hInstance, listItems[i].id, buf, | |
| sizeof(buf) / sizeof(buf[0])); | |
| index = SendMessageW(cb, CB_INSERTSTRING, -1, (LPARAM)buf); | |
| SendMessageW(cb, CB_SETITEMDATA, index, (LPARAM)data); | |
| } | |
| SendMessageW(cb, CB_SETCURSEL, 0, 0); | |
| } | |
| static void create_listview_columns(HWND hwnd) | |
| { | |
| HWND lv = GetDlgItem(hwnd, IDC_DETAIL_LIST); | |
| RECT rc; | |
| WCHAR buf[MAX_STRING_LEN]; | |
| LVCOLUMNW column; | |
| SendMessageW(lv, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT); | |
| GetWindowRect(lv, &rc); | |
| LoadStringW(hInstance, IDS_FIELD, buf, sizeof(buf) / sizeof(buf[0])); | |
| column.mask = LVCF_WIDTH | LVCF_TEXT; | |
| column.cx = (rc.right - rc.left) / 2 - 2; | |
| column.pszText = buf; | |
| SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column); | |
| LoadStringW(hInstance, IDS_VALUE, buf, sizeof(buf) / sizeof(buf[0])); | |
| SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column); | |
| } | |
| static void set_fields_selection(HWND hwnd, struct detail_data *data, int sel) | |
| { | |
| HWND list = GetDlgItem(hwnd, IDC_DETAIL_LIST); | |
| if (sel >= 0 && sel < sizeof(listItems) / sizeof(listItems[0])) | |
| { | |
| SendMessageW(list, LVM_DELETEALLITEMS, 0, 0); | |
| listItems[sel].add(list, data); | |
| } | |
| } | |
| static void create_cert_details_list(HWND hwnd, struct detail_data *data) | |
| { | |
| create_show_list(hwnd, data); | |
| create_listview_columns(hwnd); | |
| set_fields_selection(hwnd, data, 0); | |
| } | |
| static void add_purpose(HWND hwnd, LPCSTR oid) | |
| { | |
| HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES); | |
| PCRYPT_OID_INFO info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, | |
| sizeof(CRYPT_OID_INFO)); | |
| if (info) | |
| { | |
| char *oidCopy = HeapAlloc(GetProcessHeap(), 0, strlen(oid) + 1); | |
| if (oidCopy) | |
| { | |
| LVITEMA item; | |
| strcpy(oidCopy, oid); | |
| info->cbSize = sizeof(CRYPT_OID_INFO); | |
| info->pszOID = oidCopy; | |
| item.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM; | |
| item.state = INDEXTOSTATEIMAGEMASK(CheckBitmapIndexChecked); | |
| item.stateMask = LVIS_STATEIMAGEMASK; | |
| item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0); | |
| item.iSubItem = 0; | |
| item.lParam = (LPARAM)info; | |
| item.pszText = oidCopy; | |
| SendMessageA(lv, LVM_INSERTITEMA, 0, (LPARAM)&item); | |
| } | |
| else | |
| HeapFree(GetProcessHeap(), 0, info); | |
| } | |
| } | |
| static BOOL is_valid_oid(LPCSTR oid) | |
| { | |
| BOOL ret; | |
| if (oid[0] != '0' && oid[0] != '1' && oid[0] != '2') | |
| ret = FALSE; | |
| else if (oid[1] != '.') | |
| ret = FALSE; | |
| else if (!oid[2]) | |
| ret = FALSE; | |
| else | |
| { | |
| const char *ptr; | |
| BOOL expectNum = TRUE; | |
| for (ptr = oid + 2, ret = TRUE; ret && *ptr; ptr++) | |
| { | |
| if (expectNum) | |
| { | |
| if (!isdigit(*ptr)) | |
| ret = FALSE; | |
| else if (*(ptr + 1) == '.') | |
| expectNum = FALSE; | |
| } | |
| else | |
| { | |
| if (*ptr != '.') | |
| ret = FALSE; | |
| else if (!(*(ptr + 1))) | |
| ret = FALSE; | |
| else | |
| expectNum = TRUE; | |
| } | |
| } | |
| } | |
| return ret; | |
| } | |
| static BOOL is_oid_in_list(HWND hwnd, LPCSTR oid) | |
| { | |
| return find_oid_in_list(GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES), oid) | |
| != -1; | |
| } | |
| #define MAX_PURPOSE 255 | |
| static LRESULT CALLBACK add_purpose_dlg_proc(HWND hwnd, UINT msg, | |
| WPARAM wp, LPARAM lp) | |
| { | |
| LRESULT ret = 0; | |
| char buf[MAX_PURPOSE + 1]; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| SendMessageW(GetDlgItem(hwnd, IDC_NEW_PURPOSE), EM_SETLIMITTEXT, | |
| MAX_PURPOSE, 0); | |
| ShowScrollBar(GetDlgItem(hwnd, IDC_NEW_PURPOSE), SB_VERT, FALSE); | |
| SetWindowLongPtrW(hwnd, DWLP_USER, lp); | |
| break; | |
| case WM_COMMAND: | |
| switch (HIWORD(wp)) | |
| { | |
| case EN_CHANGE: | |
| if (LOWORD(wp) == IDC_NEW_PURPOSE) | |
| { | |
| /* Show/hide scroll bar on description depending on how much | |
| * text it has. | |
| */ | |
| HWND description = GetDlgItem(hwnd, IDC_NEW_PURPOSE); | |
| int lines = SendMessageW(description, EM_GETLINECOUNT, 0, 0); | |
| ShowScrollBar(description, SB_VERT, lines > 1); | |
| } | |
| break; | |
| case BN_CLICKED: | |
| switch (LOWORD(wp)) | |
| { | |
| case IDOK: | |
| SendMessageA(GetDlgItem(hwnd, IDC_NEW_PURPOSE), WM_GETTEXT, | |
| sizeof(buf) / sizeof(buf[0]), (LPARAM)buf); | |
| if (!buf[0]) | |
| { | |
| /* An empty purpose is the same as cancelling */ | |
| EndDialog(hwnd, IDCANCEL); | |
| ret = TRUE; | |
| } | |
| else if (!is_valid_oid(buf)) | |
| { | |
| WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN]; | |
| LoadStringW(hInstance, IDS_CERTIFICATE_PURPOSE_ERROR, error, | |
| sizeof(error) / sizeof(error[0])); | |
| LoadStringW(hInstance, IDS_CERTIFICATE_PROPERTIES, title, | |
| sizeof(title) / sizeof(title[0])); | |
| MessageBoxW(hwnd, error, title, MB_ICONERROR | MB_OK); | |
| } | |
| else if (is_oid_in_list( | |
| (HWND)GetWindowLongPtrW(hwnd, DWLP_USER), buf)) | |
| { | |
| WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN]; | |
| LoadStringW(hInstance, IDS_CERTIFICATE_PURPOSE_EXISTS, | |
| error, sizeof(error) / sizeof(error[0])); | |
| LoadStringW(hInstance, IDS_CERTIFICATE_PROPERTIES, title, | |
| sizeof(title) / sizeof(title[0])); | |
| MessageBoxW(hwnd, error, title, MB_ICONEXCLAMATION | MB_OK); | |
| } | |
| else | |
| { | |
| HWND parent = (HWND)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| add_purpose(parent, buf); | |
| EndDialog(hwnd, wp); | |
| ret = TRUE; | |
| } | |
| break; | |
| case IDCANCEL: | |
| EndDialog(hwnd, wp); | |
| ret = TRUE; | |
| break; | |
| } | |
| break; | |
| } | |
| break; | |
| } | |
| return ret; | |
| } | |
| static WCHAR *get_cert_property_as_string(PCCERT_CONTEXT cert, DWORD prop) | |
| { | |
| WCHAR *name = NULL; | |
| DWORD cb; | |
| if (CertGetCertificateContextProperty(cert, prop, NULL, &cb)) | |
| { | |
| name = HeapAlloc(GetProcessHeap(), 0, cb); | |
| if (name) | |
| { | |
| if (!CertGetCertificateContextProperty(cert, prop, name, &cb)) | |
| { | |
| HeapFree(GetProcessHeap(), 0, name); | |
| name = NULL; | |
| } | |
| } | |
| } | |
| return name; | |
| } | |
| static void redraw_states(HWND list, BOOL enabled) | |
| { | |
| int items = SendMessageW(list, LVM_GETITEMCOUNT, 0, 0), i; | |
| for (i = 0; i < items; i++) | |
| { | |
| BOOL change = FALSE; | |
| int state; | |
| state = SendMessageW(list, LVM_GETITEMSTATE, i, LVIS_STATEIMAGEMASK); | |
| /* This reverses the INDEXTOSTATEIMAGEMASK shift. There doesn't appear | |
| * to be a handy macro for it. | |
| */ | |
| state >>= 12; | |
| if (enabled) | |
| { | |
| if (state == CheckBitmapIndexDisabledChecked) | |
| { | |
| state = CheckBitmapIndexChecked; | |
| change = TRUE; | |
| } | |
| if (state == CheckBitmapIndexDisabledUnchecked) | |
| { | |
| state = CheckBitmapIndexUnchecked; | |
| change = TRUE; | |
| } | |
| } | |
| else | |
| { | |
| if (state == CheckBitmapIndexChecked) | |
| { | |
| state = CheckBitmapIndexDisabledChecked; | |
| change = TRUE; | |
| } | |
| if (state == CheckBitmapIndexUnchecked) | |
| { | |
| state = CheckBitmapIndexDisabledUnchecked; | |
| change = TRUE; | |
| } | |
| } | |
| if (change) | |
| { | |
| LVITEMW item; | |
| item.state = INDEXTOSTATEIMAGEMASK(state); | |
| item.stateMask = LVIS_STATEIMAGEMASK; | |
| SendMessageW(list, LVM_SETITEMSTATE, i, (LPARAM)&item); | |
| } | |
| } | |
| } | |
| typedef enum { | |
| PurposeEnableAll = 0, | |
| PurposeDisableAll, | |
| PurposeEnableSelected | |
| } PurposeSelection; | |
| static void select_purposes(HWND hwnd, PurposeSelection selection) | |
| { | |
| HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES); | |
| switch (selection) | |
| { | |
| case PurposeEnableAll: | |
| case PurposeDisableAll: | |
| EnableWindow(lv, FALSE); | |
| redraw_states(lv, FALSE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_ADD_PURPOSE), FALSE); | |
| break; | |
| case PurposeEnableSelected: | |
| EnableWindow(lv, TRUE); | |
| redraw_states(lv, TRUE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_ADD_PURPOSE), TRUE); | |
| } | |
| } | |
| struct edit_cert_data | |
| { | |
| PCCERT_CONTEXT cert; | |
| BOOL *pfPropertiesChanged; | |
| HIMAGELIST imageList; | |
| }; | |
| static void show_cert_usages(HWND hwnd, struct edit_cert_data *data) | |
| { | |
| PCCERT_CONTEXT cert = data->cert; | |
| HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES); | |
| PCERT_ENHKEY_USAGE usage; | |
| DWORD size; | |
| RECT rc; | |
| LVCOLUMNW column; | |
| PurposeSelection purposeSelection = PurposeEnableAll; | |
| GetWindowRect(lv, &rc); | |
| column.mask = LVCF_WIDTH; | |
| column.cx = rc.right - rc.left; | |
| SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column); | |
| SendMessageW(lv, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM)data->imageList); | |
| /* Get enhanced key usage. Have to check for a property and an extension | |
| * separately, because CertGetEnhancedKeyUsage will succeed and return an | |
| * empty usage if neither is set. Unfortunately an empty usage implies | |
| * no usage is allowed, so we have to distinguish between the two cases. | |
| */ | |
| if (CertGetEnhancedKeyUsage(cert, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, | |
| NULL, &size)) | |
| { | |
| usage = HeapAlloc(GetProcessHeap(), 0, size); | |
| if (!CertGetEnhancedKeyUsage(cert, | |
| CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, usage, &size)) | |
| { | |
| HeapFree(GetProcessHeap(), 0, usage); | |
| usage = NULL; | |
| } | |
| else if (usage->cUsageIdentifier) | |
| purposeSelection = PurposeEnableSelected; | |
| else | |
| purposeSelection = PurposeDisableAll; | |
| } | |
| else if (CertGetEnhancedKeyUsage(cert, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, | |
| NULL, &size)) | |
| { | |
| usage = HeapAlloc(GetProcessHeap(), 0, size); | |
| if (!CertGetEnhancedKeyUsage(cert, | |
| CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, usage, &size)) | |
| { | |
| HeapFree(GetProcessHeap(), 0, usage); | |
| usage = NULL; | |
| } | |
| else if (usage->cUsageIdentifier) | |
| purposeSelection = PurposeEnableAll; | |
| else | |
| purposeSelection = PurposeDisableAll; | |
| } | |
| else | |
| { | |
| purposeSelection = PurposeEnableAll; | |
| usage = NULL; | |
| } | |
| if (usage) | |
| { | |
| DWORD i; | |
| for (i = 0; i < usage->cUsageIdentifier; i++) | |
| { | |
| PCCRYPT_OID_INFO info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, | |
| usage->rgpszUsageIdentifier[i], CRYPT_ENHKEY_USAGE_OID_GROUP_ID); | |
| if (info) | |
| add_known_usage(lv, info, CheckBitmapIndexDisabledChecked); | |
| else | |
| add_purpose(hwnd, usage->rgpszUsageIdentifier[i]); | |
| } | |
| HeapFree(GetProcessHeap(), 0, usage); | |
| } | |
| else | |
| add_known_usages_to_list(lv, CheckBitmapIndexDisabledChecked); | |
| select_purposes(hwnd, purposeSelection); | |
| SendMessageW(GetDlgItem(hwnd, IDC_ENABLE_ALL_PURPOSES + purposeSelection), | |
| BM_CLICK, 0, 0); | |
| } | |
| static void set_general_cert_properties(HWND hwnd, struct edit_cert_data *data) | |
| { | |
| PCCERT_CONTEXT cert = data->cert; | |
| WCHAR *str; | |
| if ((str = get_cert_property_as_string(cert, CERT_FRIENDLY_NAME_PROP_ID))) | |
| { | |
| SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), WM_SETTEXT, 0, | |
| (LPARAM)str); | |
| HeapFree(GetProcessHeap(), 0, str); | |
| } | |
| if ((str = get_cert_property_as_string(cert, CERT_DESCRIPTION_PROP_ID))) | |
| { | |
| SendMessageW(GetDlgItem(hwnd, IDC_DESCRIPTION), WM_SETTEXT, 0, | |
| (LPARAM)str); | |
| HeapFree(GetProcessHeap(), 0, str); | |
| } | |
| show_cert_usages(hwnd, data); | |
| } | |
| static void set_cert_string_property(PCCERT_CONTEXT cert, DWORD prop, | |
| LPWSTR str) | |
| { | |
| if (str && strlenW(str)) | |
| { | |
| CRYPT_DATA_BLOB blob; | |
| blob.pbData = (BYTE *)str; | |
| blob.cbData = (strlenW(str) + 1) * sizeof(WCHAR); | |
| CertSetCertificateContextProperty(cert, prop, 0, &blob); | |
| } | |
| else | |
| CertSetCertificateContextProperty(cert, prop, 0, NULL); | |
| } | |
| #define WM_REFRESH_VIEW WM_USER + 0 | |
| static BOOL CALLBACK refresh_propsheet_pages(HWND hwnd, LPARAM lParam) | |
| { | |
| if ((GetClassLongW(hwnd, GCW_ATOM) == WC_DIALOG)) | |
| SendMessageW(hwnd, WM_REFRESH_VIEW, 0, 0); | |
| return TRUE; | |
| } | |
| #define MAX_FRIENDLY_NAME 40 | |
| #define MAX_DESCRIPTION 255 | |
| static void apply_general_changes(HWND hwnd) | |
| { | |
| WCHAR buf[MAX_DESCRIPTION + 1]; | |
| struct edit_cert_data *data = | |
| (struct edit_cert_data *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), WM_GETTEXT, | |
| sizeof(buf) / sizeof(buf[0]), (LPARAM)buf); | |
| set_cert_string_property(data->cert, CERT_FRIENDLY_NAME_PROP_ID, buf); | |
| SendMessageW(GetDlgItem(hwnd, IDC_DESCRIPTION), WM_GETTEXT, | |
| sizeof(buf) / sizeof(buf[0]), (LPARAM)buf); | |
| set_cert_string_property(data->cert, CERT_DESCRIPTION_PROP_ID, buf); | |
| if (IsDlgButtonChecked(hwnd, IDC_ENABLE_ALL_PURPOSES)) | |
| { | |
| /* Setting a NULL usage removes the enhanced key usage property. */ | |
| CertSetEnhancedKeyUsage(data->cert, NULL); | |
| } | |
| else if (IsDlgButtonChecked(hwnd, IDC_DISABLE_ALL_PURPOSES)) | |
| { | |
| CERT_ENHKEY_USAGE usage = { 0, NULL }; | |
| CertSetEnhancedKeyUsage(data->cert, &usage); | |
| } | |
| else if (IsDlgButtonChecked(hwnd, IDC_ENABLE_SELECTED_PURPOSES)) | |
| { | |
| HWND lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES); | |
| CERT_ENHKEY_USAGE usage = { 0, NULL }; | |
| int purposes = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0), i; | |
| LVITEMW item; | |
| item.mask = LVIF_STATE | LVIF_PARAM; | |
| item.iSubItem = 0; | |
| item.stateMask = LVIS_STATEIMAGEMASK; | |
| for (i = 0; i < purposes; i++) | |
| { | |
| item.iItem = i; | |
| if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item)) | |
| { | |
| int state = item.state >> 12; | |
| if (state == CheckBitmapIndexChecked) | |
| { | |
| CRYPT_OID_INFO *info = (CRYPT_OID_INFO *)item.lParam; | |
| if (usage.cUsageIdentifier) | |
| usage.rgpszUsageIdentifier = | |
| HeapReAlloc(GetProcessHeap(), 0, | |
| usage.rgpszUsageIdentifier, | |
| (usage.cUsageIdentifier + 1) * sizeof(LPSTR)); | |
| else | |
| usage.rgpszUsageIdentifier = | |
| HeapAlloc(GetProcessHeap(), 0, sizeof(LPSTR)); | |
| if (usage.rgpszUsageIdentifier) | |
| usage.rgpszUsageIdentifier[usage.cUsageIdentifier++] = | |
| (LPSTR)info->pszOID; | |
| } | |
| } | |
| } | |
| CertSetEnhancedKeyUsage(data->cert, &usage); | |
| HeapFree(GetProcessHeap(), 0, usage.rgpszUsageIdentifier); | |
| } | |
| EnumChildWindows(GetParent(GetParent(hwnd)), refresh_propsheet_pages, 0); | |
| if (data->pfPropertiesChanged) | |
| *data->pfPropertiesChanged = TRUE; | |
| } | |
| static LRESULT CALLBACK cert_properties_general_dlg_proc(HWND hwnd, UINT msg, | |
| WPARAM wp, LPARAM lp) | |
| { | |
| PROPSHEETPAGEW *page; | |
| TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp); | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| HWND description = GetDlgItem(hwnd, IDC_DESCRIPTION); | |
| struct detail_data *detailData; | |
| struct edit_cert_data *editData; | |
| page = (PROPSHEETPAGEW *)lp; | |
| detailData = (struct detail_data *)page->lParam; | |
| SendMessageW(GetDlgItem(hwnd, IDC_FRIENDLY_NAME), EM_SETLIMITTEXT, | |
| MAX_FRIENDLY_NAME, 0); | |
| SendMessageW(description, EM_SETLIMITTEXT, MAX_DESCRIPTION, 0); | |
| ShowScrollBar(description, SB_VERT, FALSE); | |
| editData = HeapAlloc(GetProcessHeap(), 0, | |
| sizeof(struct edit_cert_data)); | |
| if (editData) | |
| { | |
| editData->imageList = ImageList_Create(16, 16, | |
| ILC_COLOR4 | ILC_MASK, 4, 0); | |
| if (editData->imageList) | |
| { | |
| HBITMAP bmp; | |
| COLORREF backColor = RGB(255, 0, 255); | |
| bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_CHECKS)); | |
| ImageList_AddMasked(editData->imageList, bmp, backColor); | |
| DeleteObject(bmp); | |
| ImageList_SetBkColor(editData->imageList, CLR_NONE); | |
| } | |
| editData->cert = detailData->pCertViewInfo->pCertContext; | |
| editData->pfPropertiesChanged = detailData->pfPropertiesChanged; | |
| SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)editData); | |
| set_general_cert_properties(hwnd, editData); | |
| } | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| NMITEMACTIVATE *nm; | |
| switch (hdr->code) | |
| { | |
| case NM_CLICK: | |
| nm = (NMITEMACTIVATE *)lp; | |
| toggle_usage(hwnd, nm->iItem); | |
| SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0); | |
| break; | |
| case PSN_APPLY: | |
| apply_general_changes(hwnd); | |
| break; | |
| } | |
| break; | |
| } | |
| case WM_COMMAND: | |
| switch (HIWORD(wp)) | |
| { | |
| case EN_CHANGE: | |
| SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0); | |
| if (LOWORD(wp) == IDC_DESCRIPTION) | |
| { | |
| /* Show/hide scroll bar on description depending on how much | |
| * text it has. | |
| */ | |
| HWND description = GetDlgItem(hwnd, IDC_DESCRIPTION); | |
| int lines = SendMessageW(description, EM_GETLINECOUNT, 0, 0); | |
| ShowScrollBar(description, SB_VERT, lines > 1); | |
| } | |
| break; | |
| case BN_CLICKED: | |
| switch (LOWORD(wp)) | |
| { | |
| case IDC_ADD_PURPOSE: | |
| if (DialogBoxParamW(hInstance, | |
| MAKEINTRESOURCEW(IDD_ADD_CERT_PURPOSE), hwnd, | |
| add_purpose_dlg_proc, (LPARAM)hwnd) == IDOK) | |
| SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0); | |
| break; | |
| case IDC_ENABLE_ALL_PURPOSES: | |
| case IDC_DISABLE_ALL_PURPOSES: | |
| case IDC_ENABLE_SELECTED_PURPOSES: | |
| SendMessageW(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0); | |
| select_purposes(hwnd, LOWORD(wp) - IDC_ENABLE_ALL_PURPOSES); | |
| break; | |
| } | |
| break; | |
| } | |
| break; | |
| } | |
| return 0; | |
| } | |
| static UINT CALLBACK cert_properties_general_callback(HWND hwnd, UINT msg, | |
| PROPSHEETPAGEW *page) | |
| { | |
| HWND lv; | |
| int cItem, i; | |
| struct edit_cert_data *data; | |
| switch (msg) | |
| { | |
| case PSPCB_RELEASE: | |
| lv = GetDlgItem(hwnd, IDC_CERTIFICATE_USAGES); | |
| cItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0); | |
| for (i = 0; i < cItem; i++) | |
| { | |
| LVITEMW item; | |
| item.mask = LVIF_PARAM; | |
| item.iItem = i; | |
| item.iSubItem = 0; | |
| if (SendMessageW(lv, LVM_GETITEMW, 0, (LPARAM)&item) && item.lParam) | |
| { | |
| PCRYPT_OID_INFO info = (PCRYPT_OID_INFO)item.lParam; | |
| if (info->cbSize == sizeof(CRYPT_OID_INFO) && !info->dwGroupId) | |
| { | |
| HeapFree(GetProcessHeap(), 0, (LPSTR)info->pszOID); | |
| HeapFree(GetProcessHeap(), 0, info); | |
| } | |
| } | |
| } | |
| data = (struct edit_cert_data *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if (data) | |
| { | |
| ImageList_Destroy(data->imageList); | |
| HeapFree(GetProcessHeap(), 0, data); | |
| } | |
| break; | |
| } | |
| return 1; | |
| } | |
| static void show_edit_cert_properties_dialog(HWND parent, | |
| struct detail_data *data) | |
| { | |
| PROPSHEETHEADERW hdr; | |
| PROPSHEETPAGEW page; /* FIXME: need to add a cross-certificate page */ | |
| TRACE("(%p)\n", data); | |
| memset(&page, 0, sizeof(PROPSHEETPAGEW)); | |
| page.dwSize = sizeof(page); | |
| page.dwFlags = PSP_USECALLBACK; | |
| page.pfnCallback = cert_properties_general_callback; | |
| page.hInstance = hInstance; | |
| page.u.pszTemplate = MAKEINTRESOURCEW(IDD_CERT_PROPERTIES_GENERAL); | |
| page.pfnDlgProc = cert_properties_general_dlg_proc; | |
| page.lParam = (LPARAM)data; | |
| memset(&hdr, 0, sizeof(hdr)); | |
| hdr.dwSize = sizeof(hdr); | |
| hdr.hwndParent = parent; | |
| hdr.dwFlags = PSH_PROPSHEETPAGE; | |
| hdr.hInstance = hInstance; | |
| hdr.pszCaption = MAKEINTRESOURCEW(IDS_CERTIFICATE_PROPERTIES); | |
| hdr.u3.ppsp = &page; | |
| hdr.nPages = 1; | |
| PropertySheetW(&hdr); | |
| } | |
| static void free_detail_fields(struct detail_data *data) | |
| { | |
| DWORD i; | |
| for (i = 0; i < data->cFields; i++) | |
| HeapFree(GetProcessHeap(), 0, data->fields[i].detailed_value); | |
| HeapFree(GetProcessHeap(), 0, data->fields); | |
| data->fields = NULL; | |
| data->cFields = 0; | |
| } | |
| static void refresh_details_view(HWND hwnd) | |
| { | |
| HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT); | |
| int curSel; | |
| struct detail_data *data; | |
| curSel = SendMessageW(cb, CB_GETCURSEL, 0, 0); | |
| /* Actually, any index will do, since they all store the same data value */ | |
| data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA, curSel, 0); | |
| free_detail_fields(data); | |
| set_fields_selection(hwnd, data, curSel); | |
| } | |
| static LRESULT CALLBACK detail_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| PROPSHEETPAGEW *page; | |
| struct detail_data *data; | |
| TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp); | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| page = (PROPSHEETPAGEW *)lp; | |
| data = (struct detail_data *)page->lParam; | |
| create_cert_details_list(hwnd, data); | |
| if (!(data->pCertViewInfo->dwFlags & CRYPTUI_ENABLE_EDITPROPERTIES)) | |
| EnableWindow(GetDlgItem(hwnd, IDC_EDITPROPERTIES), FALSE); | |
| if (data->pCertViewInfo->dwFlags & CRYPTUI_DISABLE_EXPORT) | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT), FALSE); | |
| break; | |
| case WM_NOTIFY: | |
| { | |
| NMITEMACTIVATE *nm; | |
| HWND list = GetDlgItem(hwnd, IDC_DETAIL_LIST); | |
| nm = (NMITEMACTIVATE*)lp; | |
| if (nm->hdr.hwndFrom == list && nm->uNewState & LVN_ITEMACTIVATE | |
| && nm->hdr.code == LVN_ITEMCHANGED) | |
| { | |
| data = (struct detail_data *)nm->lParam; | |
| if (nm->iItem >= 0 && data && nm->iItem < data->cFields) | |
| { | |
| WCHAR buf[MAX_STRING_LEN], *val = NULL; | |
| HWND valueCtl = GetDlgItem(hwnd, IDC_DETAIL_VALUE); | |
| if (data->fields[nm->iItem].create) | |
| val = data->fields[nm->iItem].create( | |
| data->pCertViewInfo->pCertContext, | |
| data->fields[nm->iItem].param); | |
| else | |
| { | |
| LVITEMW item; | |
| int res; | |
| item.cchTextMax = sizeof(buf) / sizeof(buf[0]); | |
| item.mask = LVIF_TEXT; | |
| item.pszText = buf; | |
| item.iItem = nm->iItem; | |
| item.iSubItem = 1; | |
| res = SendMessageW(list, LVM_GETITEMW, 0, (LPARAM)&item); | |
| if (res) | |
| val = buf; | |
| } | |
| /* Select all the text in the control, the next update will | |
| * replace it | |
| */ | |
| SendMessageW(valueCtl, EM_SETSEL, 0, -1); | |
| add_unformatted_text_to_control(valueCtl, val, | |
| val ? strlenW(val) : 0); | |
| if (val != buf) | |
| HeapFree(GetProcessHeap(), 0, val); | |
| } | |
| } | |
| break; | |
| } | |
| case WM_COMMAND: | |
| switch (wp) | |
| { | |
| case IDC_EXPORT: | |
| { | |
| HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT); | |
| CRYPTUI_WIZ_EXPORT_INFO info; | |
| data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA, 0, 0); | |
| info.dwSize = sizeof(info); | |
| info.pwszExportFileName = NULL; | |
| info.dwSubjectChoice = CRYPTUI_WIZ_EXPORT_CERT_CONTEXT; | |
| info.u.pCertContext = data->pCertViewInfo->pCertContext; | |
| info.cStores = 0; | |
| CryptUIWizExport(0, hwnd, NULL, &info, NULL); | |
| break; | |
| } | |
| case IDC_EDITPROPERTIES: | |
| { | |
| HWND cb = GetDlgItem(hwnd, IDC_DETAIL_SELECT); | |
| int curSel; | |
| curSel = SendMessageW(cb, CB_GETCURSEL, 0, 0); | |
| /* Actually, any index will do, since they all store the same | |
| * data value | |
| */ | |
| data = (struct detail_data *)SendMessageW(cb, CB_GETITEMDATA, | |
| curSel, 0); | |
| show_edit_cert_properties_dialog(GetParent(hwnd), data); | |
| break; | |
| } | |
| case ((CBN_SELCHANGE << 16) | IDC_DETAIL_SELECT): | |
| refresh_details_view(hwnd); | |
| break; | |
| } | |
| break; | |
| case WM_REFRESH_VIEW: | |
| refresh_details_view(hwnd); | |
| break; | |
| } | |
| return 0; | |
| } | |
| static UINT CALLBACK detail_callback(HWND hwnd, UINT msg, | |
| PROPSHEETPAGEW *page) | |
| { | |
| struct detail_data *data; | |
| switch (msg) | |
| { | |
| case PSPCB_RELEASE: | |
| data = (struct detail_data *)page->lParam; | |
| free_detail_fields(data); | |
| HeapFree(GetProcessHeap(), 0, data); | |
| break; | |
| } | |
| return 0; | |
| } | |
| static BOOL init_detail_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo, | |
| BOOL *pfPropertiesChanged, PROPSHEETPAGEW *page) | |
| { | |
| BOOL ret; | |
| struct detail_data *data = HeapAlloc(GetProcessHeap(), 0, | |
| sizeof(struct detail_data)); | |
| if (data) | |
| { | |
| data->pCertViewInfo = pCertViewInfo; | |
| data->pfPropertiesChanged = pfPropertiesChanged; | |
| data->cFields = 0; | |
| data->fields = NULL; | |
| memset(page, 0, sizeof(PROPSHEETPAGEW)); | |
| page->dwSize = sizeof(PROPSHEETPAGEW); | |
| page->dwFlags = PSP_USECALLBACK; | |
| page->pfnCallback = detail_callback; | |
| page->hInstance = hInstance; | |
| page->u.pszTemplate = MAKEINTRESOURCEW(IDD_DETAIL); | |
| page->pfnDlgProc = detail_dlg_proc; | |
| page->lParam = (LPARAM)data; | |
| ret = TRUE; | |
| } | |
| else | |
| ret = FALSE; | |
| return ret; | |
| } | |
| struct hierarchy_data | |
| { | |
| PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo; | |
| HIMAGELIST imageList; | |
| DWORD selectedCert; | |
| }; | |
| static LPARAM index_to_lparam(struct hierarchy_data *data, DWORD index) | |
| { | |
| CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain( | |
| (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData, | |
| data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner, | |
| data->pCertViewInfo->idxCounterSigner); | |
| /* Takes advantage of the fact that a pointer is 32-bit aligned, and | |
| * therefore always even. | |
| */ | |
| if (index == provSigner->csCertChain - 1) | |
| return (LPARAM)data; | |
| return index << 1 | 1; | |
| } | |
| static inline DWORD lparam_to_index(struct hierarchy_data *data, LPARAM lp) | |
| { | |
| CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain( | |
| (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData, | |
| data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner, | |
| data->pCertViewInfo->idxCounterSigner); | |
| if (!(lp & 1)) | |
| return provSigner->csCertChain - 1; | |
| return lp >> 1; | |
| } | |
| static struct hierarchy_data *get_hierarchy_data_from_tree_item(HWND tree, | |
| HTREEITEM hItem) | |
| { | |
| struct hierarchy_data *data = NULL; | |
| HTREEITEM root = NULL; | |
| do { | |
| HTREEITEM parent = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, | |
| TVGN_PARENT, (LPARAM)hItem); | |
| if (!parent) | |
| root = hItem; | |
| hItem = parent; | |
| } while (hItem); | |
| if (root) | |
| { | |
| TVITEMW item; | |
| item.mask = TVIF_PARAM; | |
| item.hItem = root; | |
| SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item); | |
| data = (struct hierarchy_data *)item.lParam; | |
| } | |
| return data; | |
| } | |
| static WCHAR *get_cert_display_name(PCCERT_CONTEXT cert) | |
| { | |
| WCHAR *name = get_cert_property_as_string(cert, CERT_FRIENDLY_NAME_PROP_ID); | |
| if (!name) | |
| name = get_cert_name_string(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0); | |
| return name; | |
| } | |
| static void show_cert_chain(HWND hwnd, struct hierarchy_data *data) | |
| { | |
| HWND tree = GetDlgItem(hwnd, IDC_CERTPATH); | |
| CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain( | |
| (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData, | |
| data->pCertViewInfo->idxSigner, data->pCertViewInfo->fCounterSigner, | |
| data->pCertViewInfo->idxCounterSigner); | |
| DWORD i; | |
| HTREEITEM parent = NULL; | |
| SendMessageW(tree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)data->imageList); | |
| for (i = provSigner->csCertChain; i; i--) | |
| { | |
| LPWSTR name; | |
| name = get_cert_display_name(provSigner->pasCertChain[i - 1].pCert); | |
| if (name) | |
| { | |
| TVINSERTSTRUCTW tvis; | |
| tvis.hParent = parent; | |
| tvis.hInsertAfter = TVI_LAST; | |
| tvis.u.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_IMAGE | | |
| TVIF_SELECTEDIMAGE | TVIF_PARAM; | |
| tvis.u.item.pszText = name; | |
| tvis.u.item.state = TVIS_EXPANDED; | |
| tvis.u.item.stateMask = TVIS_EXPANDED; | |
| if (i == 1 && | |
| (provSigner->pChainContext->TrustStatus.dwErrorStatus & | |
| CERT_TRUST_IS_PARTIAL_CHAIN)) | |
| { | |
| /* The root of the chain has a special case: if the chain is | |
| * a partial chain, the icon is a warning icon rather than an | |
| * error icon. | |
| */ | |
| tvis.u.item.iImage = 2; | |
| } | |
| else if (provSigner->pasCertChain[i - 1].pChainElement->TrustStatus. | |
| dwErrorStatus == 0) | |
| tvis.u.item.iImage = 0; | |
| else | |
| tvis.u.item.iImage = 1; | |
| tvis.u.item.iSelectedImage = tvis.u.item.iImage; | |
| tvis.u.item.lParam = index_to_lparam(data, i - 1); | |
| parent = (HTREEITEM)SendMessageW(tree, TVM_INSERTITEMW, 0, | |
| (LPARAM)&tvis); | |
| HeapFree(GetProcessHeap(), 0, name); | |
| } | |
| } | |
| } | |
| static void set_certificate_status(HWND hwnd, const CRYPT_PROVIDER_CERT *cert) | |
| { | |
| /* Select all the text in the control, the next update will replace it */ | |
| SendMessageW(hwnd, EM_SETSEL, 0, -1); | |
| /* Set the highest priority error messages first. */ | |
| if (!(cert->dwConfidence & CERT_CONFIDENCE_SIG)) | |
| add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_SIGNATURE); | |
| else if (!(cert->dwConfidence & CERT_CONFIDENCE_TIME)) | |
| add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_TIME); | |
| else if (!(cert->dwConfidence & CERT_CONFIDENCE_TIMENEST)) | |
| add_string_resource_to_control(hwnd, IDS_CERTIFICATE_BAD_TIMENEST); | |
| else if (cert->dwRevokedReason) | |
| add_string_resource_to_control(hwnd, IDS_CERTIFICATE_REVOKED); | |
| else | |
| add_string_resource_to_control(hwnd, IDS_CERTIFICATE_VALID); | |
| } | |
| static void set_certificate_status_for_end_cert(HWND hwnd, | |
| PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo) | |
| { | |
| HWND status = GetDlgItem(hwnd, IDC_CERTIFICATESTATUSTEXT); | |
| CRYPT_PROVIDER_SGNR *provSigner = WTHelperGetProvSignerFromChain( | |
| (CRYPT_PROVIDER_DATA *)pCertViewInfo->u.pCryptProviderData, | |
| pCertViewInfo->idxSigner, pCertViewInfo->fCounterSigner, | |
| pCertViewInfo->idxCounterSigner); | |
| CRYPT_PROVIDER_CERT *provCert = WTHelperGetProvCertFromChain(provSigner, | |
| pCertViewInfo->idxCert); | |
| set_certificate_status(status, provCert); | |
| } | |
| static void show_cert_hierarchy(HWND hwnd, struct hierarchy_data *data) | |
| { | |
| /* Disable view certificate button until a certificate is selected */ | |
| EnableWindow(GetDlgItem(hwnd, IDC_VIEWCERTIFICATE), FALSE); | |
| show_cert_chain(hwnd, data); | |
| set_certificate_status_for_end_cert(hwnd, data->pCertViewInfo); | |
| } | |
| static void show_dialog_for_selected_cert(HWND hwnd) | |
| { | |
| HWND tree = GetDlgItem(hwnd, IDC_CERTPATH); | |
| TVITEMW item; | |
| struct hierarchy_data *data; | |
| DWORD selection; | |
| memset(&item, 0, sizeof(item)); | |
| item.mask = TVIF_HANDLE | TVIF_PARAM; | |
| item.hItem = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_CARET, 0); | |
| SendMessageW(tree, TVM_GETITEMW, 0, (LPARAM)&item); | |
| data = get_hierarchy_data_from_tree_item(tree, item.hItem); | |
| selection = lparam_to_index(data, item.lParam); | |
| if (selection != 0) | |
| { | |
| CRYPT_PROVIDER_SGNR *provSigner; | |
| CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo; | |
| BOOL changed = FALSE; | |
| provSigner = WTHelperGetProvSignerFromChain( | |
| (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData, | |
| data->pCertViewInfo->idxSigner, | |
| data->pCertViewInfo->fCounterSigner, | |
| data->pCertViewInfo->idxCounterSigner); | |
| memset(&viewInfo, 0, sizeof(viewInfo)); | |
| viewInfo.dwSize = sizeof(viewInfo); | |
| viewInfo.dwFlags = data->pCertViewInfo->dwFlags; | |
| viewInfo.szTitle = data->pCertViewInfo->szTitle; | |
| viewInfo.pCertContext = provSigner->pasCertChain[selection].pCert; | |
| viewInfo.cStores = data->pCertViewInfo->cStores; | |
| viewInfo.rghStores = data->pCertViewInfo->rghStores; | |
| viewInfo.cPropSheetPages = data->pCertViewInfo->cPropSheetPages; | |
| viewInfo.rgPropSheetPages = data->pCertViewInfo->rgPropSheetPages; | |
| viewInfo.nStartPage = data->pCertViewInfo->nStartPage; | |
| CryptUIDlgViewCertificateW(&viewInfo, &changed); | |
| if (changed) | |
| { | |
| /* Delete the contents of the tree */ | |
| SendMessageW(tree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT); | |
| /* Reinitialize the tree */ | |
| show_cert_hierarchy(hwnd, data); | |
| } | |
| } | |
| } | |
| static LRESULT CALLBACK hierarchy_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| PROPSHEETPAGEW *page; | |
| struct hierarchy_data *data; | |
| LRESULT ret = 0; | |
| HWND tree = GetDlgItem(hwnd, IDC_CERTPATH); | |
| TRACE("(%p, %08x, %08lx, %08lx)\n", hwnd, msg, wp, lp); | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| page = (PROPSHEETPAGEW *)lp; | |
| data = (struct hierarchy_data *)page->lParam; | |
| show_cert_hierarchy(hwnd, data); | |
| break; | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr; | |
| hdr = (NMHDR *)lp; | |
| switch (hdr->code) | |
| { | |
| case TVN_SELCHANGEDW: | |
| { | |
| NMTREEVIEWW *nm = (NMTREEVIEWW*)lp; | |
| DWORD selection; | |
| CRYPT_PROVIDER_SGNR *provSigner; | |
| data = get_hierarchy_data_from_tree_item(tree, nm->itemNew.hItem); | |
| selection = lparam_to_index(data, nm->itemNew.lParam); | |
| provSigner = WTHelperGetProvSignerFromChain( | |
| (CRYPT_PROVIDER_DATA *)data->pCertViewInfo->u.pCryptProviderData, | |
| data->pCertViewInfo->idxSigner, | |
| data->pCertViewInfo->fCounterSigner, | |
| data->pCertViewInfo->idxCounterSigner); | |
| EnableWindow(GetDlgItem(hwnd, IDC_VIEWCERTIFICATE), selection != 0); | |
| set_certificate_status(GetDlgItem(hwnd, IDC_CERTIFICATESTATUSTEXT), | |
| &provSigner->pasCertChain[selection]); | |
| break; | |
| } | |
| case NM_DBLCLK: | |
| show_dialog_for_selected_cert(hwnd); | |
| SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1); | |
| ret = 1; | |
| break; | |
| } | |
| break; | |
| } | |
| case WM_COMMAND: | |
| switch (wp) | |
| { | |
| case IDC_VIEWCERTIFICATE: | |
| show_dialog_for_selected_cert(hwnd); | |
| break; | |
| } | |
| break; | |
| case WM_REFRESH_VIEW: | |
| { | |
| TVITEMW item; | |
| /* Get hierarchy data */ | |
| memset(&item, 0, sizeof(item)); | |
| item.mask = TVIF_HANDLE | TVIF_PARAM; | |
| item.hItem = (HTREEITEM)SendMessageW(tree, TVM_GETNEXTITEM, TVGN_ROOT, | |
| 0); | |
| data = get_hierarchy_data_from_tree_item(tree, item.hItem); | |
| /* Delete the contents of the tree */ | |
| SendMessageW(tree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT); | |
| /* Reinitialize the tree */ | |
| show_cert_hierarchy(hwnd, data); | |
| break; | |
| } | |
| } | |
| return ret; | |
| } | |
| static UINT CALLBACK hierarchy_callback(HWND hwnd, UINT msg, | |
| PROPSHEETPAGEW *page) | |
| { | |
| struct hierarchy_data *data; | |
| switch (msg) | |
| { | |
| case PSPCB_RELEASE: | |
| data = (struct hierarchy_data *)page->lParam; | |
| ImageList_Destroy(data->imageList); | |
| HeapFree(GetProcessHeap(), 0, data); | |
| break; | |
| } | |
| return 0; | |
| } | |
| static BOOL init_hierarchy_page(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo, | |
| PROPSHEETPAGEW *page) | |
| { | |
| struct hierarchy_data *data = HeapAlloc(GetProcessHeap(), 0, | |
| sizeof(struct hierarchy_data)); | |
| BOOL ret = FALSE; | |
| if (data) | |
| { | |
| data->imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 2, 0); | |
| if (data->imageList) | |
| { | |
| HBITMAP bmp; | |
| COLORREF backColor = RGB(255, 0, 255); | |
| data->pCertViewInfo = pCertViewInfo; | |
| data->selectedCert = 0xffffffff; | |
| bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_SMALL_ICONS)); | |
| ImageList_AddMasked(data->imageList, bmp, backColor); | |
| DeleteObject(bmp); | |
| ImageList_SetBkColor(data->imageList, CLR_NONE); | |
| memset(page, 0, sizeof(PROPSHEETPAGEW)); | |
| page->dwSize = sizeof(PROPSHEETPAGEW); | |
| page->dwFlags = PSP_USECALLBACK; | |
| page->hInstance = hInstance; | |
| page->u.pszTemplate = MAKEINTRESOURCEW(IDD_HIERARCHY); | |
| page->pfnDlgProc = hierarchy_dlg_proc; | |
| page->lParam = (LPARAM)data; | |
| page->pfnCallback = hierarchy_callback; | |
| ret = TRUE; | |
| } | |
| else | |
| HeapFree(GetProcessHeap(), 0, data); | |
| } | |
| return ret; | |
| } | |
| static int CALLBACK cert_prop_sheet_proc(HWND hwnd, UINT msg, LPARAM lp) | |
| { | |
| RECT rc; | |
| POINT topLeft; | |
| TRACE("(%p, %08x, %08lx)\n", hwnd, msg, lp); | |
| switch (msg) | |
| { | |
| case PSCB_INITIALIZED: | |
| /* Get cancel button's position.. */ | |
| GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rc); | |
| topLeft.x = rc.left; | |
| topLeft.y = rc.top; | |
| ScreenToClient(hwnd, &topLeft); | |
| /* hide the cancel button.. */ | |
| ShowWindow(GetDlgItem(hwnd, IDCANCEL), FALSE); | |
| /* get the OK button's size.. */ | |
| GetWindowRect(GetDlgItem(hwnd, IDOK), &rc); | |
| /* and move the OK button to the cancel button's original position. */ | |
| MoveWindow(GetDlgItem(hwnd, IDOK), topLeft.x, topLeft.y, | |
| rc.right - rc.left, rc.bottom - rc.top, FALSE); | |
| GetWindowRect(GetDlgItem(hwnd, IDOK), &rc); | |
| break; | |
| } | |
| return 0; | |
| } | |
| static BOOL show_cert_dialog(PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo, | |
| CRYPT_PROVIDER_CERT *provCert, BOOL *pfPropertiesChanged) | |
| { | |
| static const WCHAR riched[] = { 'r','i','c','h','e','d','2','0',0 }; | |
| DWORD nPages; | |
| PROPSHEETPAGEW *pages; | |
| BOOL ret = FALSE; | |
| HMODULE lib = LoadLibraryW(riched); | |
| nPages = pCertViewInfo->cPropSheetPages + 1; /* one for the General tab */ | |
| if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_DETAILPAGE)) | |
| nPages++; | |
| if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_HIERARCHYPAGE)) | |
| nPages++; | |
| pages = HeapAlloc(GetProcessHeap(), 0, nPages * sizeof(PROPSHEETPAGEW)); | |
| if (pages) | |
| { | |
| PROPSHEETHEADERW hdr; | |
| CRYPTUI_INITDIALOG_STRUCT *init = NULL; | |
| DWORD i; | |
| memset(&hdr, 0, sizeof(hdr)); | |
| hdr.dwSize = sizeof(hdr); | |
| hdr.dwFlags = PSH_NOAPPLYNOW | PSH_PROPSHEETPAGE | PSH_USECALLBACK; | |
| hdr.hInstance = hInstance; | |
| if (pCertViewInfo->szTitle) | |
| hdr.pszCaption = pCertViewInfo->szTitle; | |
| else | |
| hdr.pszCaption = MAKEINTRESOURCEW(IDS_CERTIFICATE); | |
| init_general_page(pCertViewInfo, &pages[hdr.nPages++]); | |
| if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_DETAILPAGE)) | |
| { | |
| if (init_detail_page(pCertViewInfo, pfPropertiesChanged, | |
| &pages[hdr.nPages])) | |
| hdr.nPages++; | |
| } | |
| if (!(pCertViewInfo->dwFlags & CRYPTUI_HIDE_HIERARCHYPAGE)) | |
| { | |
| if (init_hierarchy_page(pCertViewInfo, &pages[hdr.nPages])) | |
| hdr.nPages++; | |
| } | |
| /* Copy each additional page, and create the init dialog struct for it | |
| */ | |
| if (pCertViewInfo->cPropSheetPages) | |
| { | |
| init = HeapAlloc(GetProcessHeap(), 0, | |
| pCertViewInfo->cPropSheetPages * | |
| sizeof(CRYPTUI_INITDIALOG_STRUCT)); | |
| if (init) | |
| { | |
| for (i = 0; i < pCertViewInfo->cPropSheetPages; i++) | |
| { | |
| memcpy(&pages[hdr.nPages + i], | |
| &pCertViewInfo->rgPropSheetPages[i], | |
| sizeof(PROPSHEETPAGEW)); | |
| init[i].lParam = pCertViewInfo->rgPropSheetPages[i].lParam; | |
| init[i].pCertContext = pCertViewInfo->pCertContext; | |
| pages[hdr.nPages + i].lParam = (LPARAM)&init[i]; | |
| } | |
| if (pCertViewInfo->nStartPage & 0x8000) | |
| { | |
| /* Start page index is relative to the number of default | |
| * pages | |
| */ | |
| hdr.u2.nStartPage = pCertViewInfo->nStartPage + hdr.nPages; | |
| } | |
| else | |
| hdr.u2.nStartPage = pCertViewInfo->nStartPage; | |
| hdr.nPages = nPages; | |
| ret = TRUE; | |
| } | |
| else | |
| SetLastError(ERROR_OUTOFMEMORY); | |
| } | |
| else | |
| { | |
| /* Ignore the relative flag if there aren't any additional pages */ | |
| hdr.u2.nStartPage = pCertViewInfo->nStartPage & 0x7fff; | |
| ret = TRUE; | |
| } | |
| if (ret) | |
| { | |
| INT_PTR l; | |
| hdr.u3.ppsp = pages; | |
| hdr.pfnCallback = cert_prop_sheet_proc; | |
| l = PropertySheetW(&hdr); | |
| if (l == 0) | |
| { | |
| SetLastError(ERROR_CANCELLED); | |
| ret = FALSE; | |
| } | |
| } | |
| HeapFree(GetProcessHeap(), 0, init); | |
| HeapFree(GetProcessHeap(), 0, pages); | |
| } | |
| else | |
| SetLastError(ERROR_OUTOFMEMORY); | |
| FreeLibrary(lib); | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * CryptUIDlgViewCertificateW (CRYPTUI.@) | |
| */ | |
| BOOL WINAPI CryptUIDlgViewCertificateW( | |
| PCCRYPTUI_VIEWCERTIFICATE_STRUCTW pCertViewInfo, BOOL *pfPropertiesChanged) | |
| { | |
| static GUID generic_cert_verify = WINTRUST_ACTION_GENERIC_CERT_VERIFY; | |
| CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo; | |
| WINTRUST_DATA wvt; | |
| WINTRUST_CERT_INFO cert; | |
| BOOL ret = FALSE; | |
| CRYPT_PROVIDER_SGNR *signer; | |
| CRYPT_PROVIDER_CERT *provCert = NULL; | |
| TRACE("(%p, %p)\n", pCertViewInfo, pfPropertiesChanged); | |
| if (pCertViewInfo->dwSize != sizeof(CRYPTUI_VIEWCERTIFICATE_STRUCTW)) | |
| { | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| return FALSE; | |
| } | |
| /* Make a local copy in case we have to call WinVerifyTrust ourselves */ | |
| memcpy(&viewInfo, pCertViewInfo, sizeof(viewInfo)); | |
| if (!viewInfo.u.hWVTStateData) | |
| { | |
| memset(&wvt, 0, sizeof(wvt)); | |
| wvt.cbStruct = sizeof(wvt); | |
| wvt.dwUIChoice = WTD_UI_NONE; | |
| if (viewInfo.dwFlags & | |
| CRYPTUI_ENABLE_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT) | |
| wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; | |
| if (viewInfo.dwFlags & CRYPTUI_ENABLE_REVOCATION_CHECK_END_CERT) | |
| wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_END_CERT; | |
| if (viewInfo.dwFlags & CRYPTUI_ENABLE_REVOCATION_CHECK_CHAIN) | |
| wvt.fdwRevocationChecks |= WTD_REVOCATION_CHECK_CHAIN; | |
| wvt.dwUnionChoice = WTD_CHOICE_CERT; | |
| memset(&cert, 0, sizeof(cert)); | |
| cert.cbStruct = sizeof(cert); | |
| cert.psCertContext = (CERT_CONTEXT *)viewInfo.pCertContext; | |
| cert.chStores = viewInfo.cStores; | |
| cert.pahStores = viewInfo.rghStores; | |
| wvt.u.pCert = &cert; | |
| wvt.dwStateAction = WTD_STATEACTION_VERIFY; | |
| WinVerifyTrust(NULL, &generic_cert_verify, &wvt); | |
| viewInfo.u.pCryptProviderData = | |
| WTHelperProvDataFromStateData(wvt.hWVTStateData); | |
| signer = WTHelperGetProvSignerFromChain( | |
| (CRYPT_PROVIDER_DATA *)viewInfo.u.pCryptProviderData, 0, FALSE, 0); | |
| provCert = WTHelperGetProvCertFromChain(signer, 0); | |
| ret = TRUE; | |
| } | |
| else | |
| { | |
| viewInfo.u.pCryptProviderData = | |
| WTHelperProvDataFromStateData(viewInfo.u.hWVTStateData); | |
| signer = WTHelperGetProvSignerFromChain( | |
| (CRYPT_PROVIDER_DATA *)viewInfo.u.pCryptProviderData, | |
| viewInfo.idxSigner, viewInfo.fCounterSigner, | |
| viewInfo.idxCounterSigner); | |
| provCert = WTHelperGetProvCertFromChain(signer, viewInfo.idxCert); | |
| ret = TRUE; | |
| } | |
| if (ret) | |
| { | |
| ret = show_cert_dialog(&viewInfo, provCert, pfPropertiesChanged); | |
| if (!viewInfo.u.hWVTStateData) | |
| { | |
| wvt.dwStateAction = WTD_STATEACTION_CLOSE; | |
| WinVerifyTrust(NULL, &generic_cert_verify, &wvt); | |
| } | |
| } | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * CryptUIDlgViewContext (CRYPTUI.@) | |
| */ | |
| BOOL WINAPI CryptUIDlgViewContext(DWORD dwContextType, LPVOID pvContext, | |
| HWND hwnd, LPCWSTR pwszTitle, DWORD dwFlags, LPVOID pvReserved) | |
| { | |
| BOOL ret; | |
| TRACE("(%d, %p, %p, %s, %08x, %p)\n", dwContextType, pvContext, hwnd, | |
| debugstr_w(pwszTitle), dwFlags, pvReserved); | |
| switch (dwContextType) | |
| { | |
| case CERT_STORE_CERTIFICATE_CONTEXT: | |
| { | |
| CRYPTUI_VIEWCERTIFICATE_STRUCTW viewInfo; | |
| memset(&viewInfo, 0, sizeof(viewInfo)); | |
| viewInfo.dwSize = sizeof(viewInfo); | |
| viewInfo.hwndParent = hwnd; | |
| viewInfo.szTitle = pwszTitle; | |
| viewInfo.pCertContext = pvContext; | |
| ret = CryptUIDlgViewCertificateW(&viewInfo, NULL); | |
| break; | |
| } | |
| default: | |
| FIXME("unimplemented for context type %d\n", dwContextType); | |
| SetLastError(E_INVALIDARG); | |
| ret = FALSE; | |
| } | |
| return ret; | |
| } | |
| /* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS | |
| * or szOID_BASIC_CONSTRAINTS2, whichever is present) to determine if it | |
| * should be a CA. If neither extension is present, returns | |
| * defaultIfNotSpecified. | |
| */ | |
| static BOOL is_ca_cert(PCCERT_CONTEXT cert, BOOL defaultIfNotSpecified) | |
| { | |
| BOOL isCA = defaultIfNotSpecified; | |
| PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS, | |
| cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension); | |
| if (ext) | |
| { | |
| CERT_BASIC_CONSTRAINTS_INFO *info; | |
| DWORD size = 0; | |
| if (CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS, | |
| ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, | |
| NULL, &info, &size)) | |
| { | |
| if (info->SubjectType.cbData == 1) | |
| isCA = info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG; | |
| LocalFree(info); | |
| } | |
| } | |
| else | |
| { | |
| ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2, | |
| cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension); | |
| if (ext) | |
| { | |
| CERT_BASIC_CONSTRAINTS2_INFO info; | |
| DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO); | |
| if (CryptDecodeObjectEx(X509_ASN_ENCODING, | |
| szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData, | |
| 0, NULL, &info, &size)) | |
| isCA = info.fCA; | |
| } | |
| } | |
| return isCA; | |
| } | |
| static HCERTSTORE choose_store_for_cert(PCCERT_CONTEXT cert) | |
| { | |
| LPCWSTR storeName; | |
| if (is_ca_cert(cert, TRUE)) | |
| storeName = ca; | |
| else | |
| storeName = addressBook; | |
| return CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0, | |
| CERT_SYSTEM_STORE_CURRENT_USER, storeName); | |
| } | |
| static BOOL import_cert(PCCERT_CONTEXT cert, HCERTSTORE hDestCertStore) | |
| { | |
| HCERTSTORE store; | |
| BOOL ret; | |
| if (!cert) | |
| { | |
| SetLastError(E_INVALIDARG); | |
| return FALSE; | |
| } | |
| if (hDestCertStore) store = hDestCertStore; | |
| else | |
| { | |
| if (!(store = choose_store_for_cert(cert))) | |
| { | |
| WARN("unable to open certificate store\n"); | |
| return FALSE; | |
| } | |
| } | |
| ret = CertAddCertificateContextToStore(store, cert, | |
| CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, NULL); | |
| if (!hDestCertStore) CertCloseStore(store, 0); | |
| return ret; | |
| } | |
| static BOOL import_crl(PCCRL_CONTEXT crl, HCERTSTORE hDestCertStore) | |
| { | |
| HCERTSTORE store; | |
| BOOL ret; | |
| if (!crl) | |
| { | |
| SetLastError(E_INVALIDARG); | |
| return FALSE; | |
| } | |
| if (hDestCertStore) store = hDestCertStore; | |
| else | |
| { | |
| if (!(store = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0, | |
| CERT_SYSTEM_STORE_CURRENT_USER, ca))) | |
| { | |
| WARN("unable to open certificate store\n"); | |
| return FALSE; | |
| } | |
| } | |
| ret = CertAddCRLContextToStore(store, crl, | |
| CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, NULL); | |
| if (!hDestCertStore) CertCloseStore(store, 0); | |
| return ret; | |
| } | |
| static BOOL import_ctl(PCCTL_CONTEXT ctl, HCERTSTORE hDestCertStore) | |
| { | |
| HCERTSTORE store; | |
| BOOL ret; | |
| if (!ctl) | |
| { | |
| SetLastError(E_INVALIDARG); | |
| return FALSE; | |
| } | |
| if (hDestCertStore) store = hDestCertStore; | |
| else | |
| { | |
| static const WCHAR trust[] = { 'T','r','u','s','t',0 }; | |
| if (!(store = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0, | |
| CERT_SYSTEM_STORE_CURRENT_USER, trust))) | |
| { | |
| WARN("unable to open certificate store\n"); | |
| return FALSE; | |
| } | |
| } | |
| ret = CertAddCTLContextToStore(store, ctl, | |
| CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, NULL); | |
| if (!hDestCertStore) CertCloseStore(store, 0); | |
| return ret; | |
| } | |
| /* Checks type, a type such as CERT_QUERY_CONTENT_CERT returned by | |
| * CryptQueryObject, against the allowed types. Returns TRUE if the | |
| * type is allowed, FALSE otherwise. | |
| */ | |
| static BOOL check_context_type(DWORD dwFlags, DWORD type) | |
| { | |
| BOOL ret; | |
| if (dwFlags & | |
| (CRYPTUI_WIZ_IMPORT_ALLOW_CERT | CRYPTUI_WIZ_IMPORT_ALLOW_CRL | | |
| CRYPTUI_WIZ_IMPORT_ALLOW_CTL)) | |
| { | |
| switch (type) | |
| { | |
| case CERT_QUERY_CONTENT_CERT: | |
| case CERT_QUERY_CONTENT_SERIALIZED_CERT: | |
| ret = dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CERT; | |
| break; | |
| case CERT_QUERY_CONTENT_CRL: | |
| case CERT_QUERY_CONTENT_SERIALIZED_CRL: | |
| ret = dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CRL; | |
| break; | |
| case CERT_QUERY_CONTENT_CTL: | |
| case CERT_QUERY_CONTENT_SERIALIZED_CTL: | |
| ret = dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CTL; | |
| break; | |
| default: | |
| /* The remaining types contain more than one type, so allow | |
| * any combination. | |
| */ | |
| ret = TRUE; | |
| } | |
| } | |
| else | |
| { | |
| /* No allowed types specified, so any type is allowed */ | |
| ret = TRUE; | |
| } | |
| if (!ret) | |
| SetLastError(E_INVALIDARG); | |
| return ret; | |
| } | |
| static void import_warning(DWORD dwFlags, HWND hwnd, LPCWSTR szTitle, | |
| int warningID) | |
| { | |
| if (!(dwFlags & CRYPTUI_WIZ_NO_UI)) | |
| { | |
| WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN]; | |
| LPCWSTR pTitle; | |
| if (szTitle) | |
| pTitle = szTitle; | |
| else | |
| { | |
| LoadStringW(hInstance, IDS_IMPORT_WIZARD, title, | |
| sizeof(title) / sizeof(title[0])); | |
| pTitle = title; | |
| } | |
| LoadStringW(hInstance, warningID, error, | |
| sizeof(error) / sizeof(error[0])); | |
| MessageBoxW(hwnd, error, pTitle, MB_ICONERROR | MB_OK); | |
| } | |
| } | |
| static void import_warn_type_mismatch(DWORD dwFlags, HWND hwnd, LPCWSTR szTitle) | |
| { | |
| import_warning(dwFlags, hwnd, szTitle, IDS_IMPORT_TYPE_MISMATCH); | |
| } | |
| static BOOL check_store_context_type(DWORD dwFlags, HCERTSTORE store) | |
| { | |
| BOOL ret; | |
| if (dwFlags & | |
| (CRYPTUI_WIZ_IMPORT_ALLOW_CERT | CRYPTUI_WIZ_IMPORT_ALLOW_CRL | | |
| CRYPTUI_WIZ_IMPORT_ALLOW_CTL)) | |
| { | |
| PCCERT_CONTEXT cert; | |
| PCCRL_CONTEXT crl; | |
| PCCTL_CONTEXT ctl; | |
| ret = TRUE; | |
| if ((cert = CertEnumCertificatesInStore(store, NULL))) | |
| { | |
| CertFreeCertificateContext(cert); | |
| if (!(dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CERT)) | |
| ret = FALSE; | |
| } | |
| if (ret && (crl = CertEnumCRLsInStore(store, NULL))) | |
| { | |
| CertFreeCRLContext(crl); | |
| if (!(dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CRL)) | |
| ret = FALSE; | |
| } | |
| if (ret && (ctl = CertEnumCTLsInStore(store, NULL))) | |
| { | |
| CertFreeCTLContext(ctl); | |
| if (!(dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CTL)) | |
| ret = FALSE; | |
| } | |
| } | |
| else | |
| ret = TRUE; | |
| if (!ret) | |
| SetLastError(E_INVALIDARG); | |
| return ret; | |
| } | |
| static BOOL import_store(DWORD dwFlags, HWND hwnd, LPCWSTR szTitle, | |
| HCERTSTORE source, HCERTSTORE dest) | |
| { | |
| BOOL ret; | |
| if ((ret = check_store_context_type(dwFlags, source))) | |
| { | |
| PCCERT_CONTEXT cert = NULL; | |
| PCCRL_CONTEXT crl = NULL; | |
| PCCTL_CONTEXT ctl = NULL; | |
| do { | |
| cert = CertEnumCertificatesInStore(source, cert); | |
| if (cert) | |
| ret = import_cert(cert, dest); | |
| } while (ret && cert); | |
| do { | |
| crl = CertEnumCRLsInStore(source, crl); | |
| if (crl) | |
| ret = import_crl(crl, dest); | |
| } while (ret && crl); | |
| do { | |
| ctl = CertEnumCTLsInStore(source, ctl); | |
| if (ctl) | |
| ret = import_ctl(ctl, dest); | |
| } while (ret && ctl); | |
| } | |
| else | |
| import_warn_type_mismatch(dwFlags, hwnd, szTitle); | |
| return ret; | |
| } | |
| static HCERTSTORE open_store_from_file(DWORD dwFlags, LPCWSTR fileName, | |
| DWORD *pContentType) | |
| { | |
| HCERTSTORE store = NULL; | |
| DWORD contentType = 0, expectedContentTypeFlags; | |
| if (dwFlags & | |
| (CRYPTUI_WIZ_IMPORT_ALLOW_CERT | CRYPTUI_WIZ_IMPORT_ALLOW_CRL | | |
| CRYPTUI_WIZ_IMPORT_ALLOW_CTL)) | |
| { | |
| expectedContentTypeFlags = | |
| CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE | | |
| CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED | | |
| CERT_QUERY_CONTENT_FLAG_PFX; | |
| if (dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CERT) | |
| expectedContentTypeFlags |= | |
| CERT_QUERY_CONTENT_FLAG_CERT | | |
| CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT; | |
| if (dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CRL) | |
| expectedContentTypeFlags |= | |
| CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL | | |
| CERT_QUERY_CONTENT_FLAG_CRL; | |
| if (dwFlags & CRYPTUI_WIZ_IMPORT_ALLOW_CTL) | |
| expectedContentTypeFlags |= | |
| CERT_QUERY_CONTENT_FLAG_CTL | | |
| CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL; | |
| } | |
| else | |
| expectedContentTypeFlags = | |
| CERT_QUERY_CONTENT_FLAG_CERT | | |
| CERT_QUERY_CONTENT_FLAG_CTL | | |
| CERT_QUERY_CONTENT_FLAG_CRL | | |
| CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE | | |
| CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT | | |
| CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL | | |
| CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL | | |
| CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED | | |
| CERT_QUERY_CONTENT_FLAG_PFX; | |
| CryptQueryObject(CERT_QUERY_OBJECT_FILE, fileName, | |
| expectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, | |
| &contentType, NULL, &store, NULL, NULL); | |
| if (pContentType) | |
| *pContentType = contentType; | |
| return store; | |
| } | |
| static BOOL import_file(DWORD dwFlags, HWND hwnd, LPCWSTR szTitle, | |
| LPCWSTR fileName, HCERTSTORE dest) | |
| { | |
| HCERTSTORE source; | |
| BOOL ret; | |
| if ((source = open_store_from_file(dwFlags, fileName, NULL))) | |
| { | |
| ret = import_store(dwFlags, hwnd, szTitle, source, dest); | |
| CertCloseStore(source, 0); | |
| } | |
| else | |
| ret = FALSE; | |
| return ret; | |
| } | |
| struct ImportWizData | |
| { | |
| HFONT titleFont; | |
| DWORD dwFlags; | |
| LPCWSTR pwszWizardTitle; | |
| CRYPTUI_WIZ_IMPORT_SRC_INFO importSrc; | |
| LPWSTR fileName; | |
| DWORD contentType; | |
| BOOL freeSource; | |
| HCERTSTORE hDestCertStore; | |
| BOOL freeDest; | |
| BOOL autoDest; | |
| BOOL success; | |
| }; | |
| static LRESULT CALLBACK import_welcome_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| LRESULT ret = 0; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| struct ImportWizData *data; | |
| PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; | |
| WCHAR fontFace[MAX_STRING_LEN]; | |
| HDC hDC = GetDC(hwnd); | |
| int height; | |
| data = (struct ImportWizData *)page->lParam; | |
| LoadStringW(hInstance, IDS_WIZARD_TITLE_FONT, fontFace, | |
| sizeof(fontFace) / sizeof(fontFace[0])); | |
| height = -MulDiv(12, GetDeviceCaps(hDC, LOGPIXELSY), 72); | |
| data->titleFont = CreateFontW(height, 0, 0, 0, FW_BOLD, 0, 0, 0, | |
| DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, | |
| DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, fontFace); | |
| SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_TITLE), WM_SETFONT, | |
| (WPARAM)data->titleFont, TRUE); | |
| ReleaseDC(hwnd, hDC); | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| switch (hdr->code) | |
| { | |
| case PSN_SETACTIVE: | |
| PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, PSWIZB_NEXT); | |
| ret = TRUE; | |
| break; | |
| } | |
| break; | |
| } | |
| } | |
| return ret; | |
| } | |
| static const WCHAR filter_cert[] = { '*','.','c','e','r',';','*','.', | |
| 'c','r','t',0 }; | |
| static const WCHAR filter_pfx[] = { '*','.','p','f','x',';','*','.', | |
| 'p','1','2',0 }; | |
| static const WCHAR filter_crl[] = { '*','.','c','r','l',0 }; | |
| static const WCHAR filter_ctl[] = { '*','.','s','t','l',0 }; | |
| static const WCHAR filter_serialized_store[] = { '*','.','s','s','t',0 }; | |
| static const WCHAR filter_cms[] = { '*','.','s','p','c',';','*','.', | |
| 'p','7','b',0 }; | |
| static const WCHAR filter_all[] = { '*','.','*',0 }; | |
| struct StringToFilter | |
| { | |
| int id; | |
| DWORD allowFlags; | |
| LPCWSTR filter; | |
| } import_filters[] = { | |
| { IDS_IMPORT_FILTER_CERT, CRYPTUI_WIZ_IMPORT_ALLOW_CERT, filter_cert }, | |
| { IDS_IMPORT_FILTER_PFX, 0, filter_pfx }, | |
| { IDS_IMPORT_FILTER_CRL, CRYPTUI_WIZ_IMPORT_ALLOW_CRL, filter_crl }, | |
| { IDS_IMPORT_FILTER_CTL, CRYPTUI_WIZ_IMPORT_ALLOW_CTL, filter_ctl }, | |
| { IDS_IMPORT_FILTER_SERIALIZED_STORE, 0, filter_serialized_store }, | |
| { IDS_IMPORT_FILTER_CMS, 0, filter_cms }, | |
| { IDS_IMPORT_FILTER_ALL, 0, filter_all }, | |
| }; | |
| static WCHAR *make_import_file_filter(DWORD dwFlags) | |
| { | |
| DWORD i; | |
| int len, totalLen = 2; | |
| LPWSTR filter = NULL, str; | |
| for (i = 0; i < sizeof(import_filters) / sizeof(import_filters[0]); i++) | |
| { | |
| if (!import_filters[i].allowFlags || !dwFlags || | |
| (dwFlags & import_filters[i].allowFlags)) | |
| { | |
| len = LoadStringW(hInstance, import_filters[i].id, (LPWSTR)&str, 0); | |
| totalLen += len + strlenW(import_filters[i].filter) + 2; | |
| } | |
| } | |
| filter = HeapAlloc(GetProcessHeap(), 0, totalLen * sizeof(WCHAR)); | |
| if (filter) | |
| { | |
| LPWSTR ptr; | |
| ptr = filter; | |
| for (i = 0; i < sizeof(import_filters) / sizeof(import_filters[0]); i++) | |
| { | |
| if (!import_filters[i].allowFlags || !dwFlags || | |
| (dwFlags & import_filters[i].allowFlags)) | |
| { | |
| len = LoadStringW(hInstance, import_filters[i].id, | |
| (LPWSTR)&str, 0); | |
| memcpy(ptr, str, len * sizeof(WCHAR)); | |
| ptr += len; | |
| *ptr++ = 0; | |
| strcpyW(ptr, import_filters[i].filter); | |
| ptr += strlenW(import_filters[i].filter) + 1; | |
| } | |
| } | |
| *ptr++ = 0; | |
| } | |
| return filter; | |
| } | |
| static BOOL import_validate_filename(HWND hwnd, struct ImportWizData *data, | |
| LPCWSTR fileName) | |
| { | |
| HANDLE file; | |
| BOOL ret = FALSE; | |
| file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, | |
| OPEN_EXISTING, 0, NULL); | |
| if (file != INVALID_HANDLE_VALUE) | |
| { | |
| HCERTSTORE source = open_store_from_file(data->dwFlags, fileName, | |
| &data->contentType); | |
| int warningID = 0; | |
| if (!source) | |
| warningID = IDS_IMPORT_BAD_FORMAT; | |
| else if (!check_store_context_type(data->dwFlags, source)) | |
| warningID = IDS_IMPORT_TYPE_MISMATCH; | |
| else | |
| { | |
| data->importSrc.dwSubjectChoice = | |
| CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_STORE; | |
| data->importSrc.u.hCertStore = source; | |
| data->freeSource = TRUE; | |
| ret = TRUE; | |
| } | |
| if (warningID) | |
| { | |
| import_warning(data->dwFlags, hwnd, data->pwszWizardTitle, | |
| warningID); | |
| } | |
| CloseHandle(file); | |
| } | |
| else | |
| { | |
| WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN]; | |
| LPCWSTR pTitle; | |
| LPWSTR msgBuf, fullError; | |
| if (data->pwszWizardTitle) | |
| pTitle = data->pwszWizardTitle; | |
| else | |
| { | |
| LoadStringW(hInstance, IDS_IMPORT_WIZARD, title, | |
| sizeof(title) / sizeof(title[0])); | |
| pTitle = title; | |
| } | |
| LoadStringW(hInstance, IDS_IMPORT_OPEN_FAILED, error, | |
| sizeof(error) / sizeof(error[0])); | |
| FormatMessageW( | |
| FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, | |
| GetLastError(), 0, (LPWSTR) &msgBuf, 0, NULL); | |
| fullError = HeapAlloc(GetProcessHeap(), 0, | |
| (strlenW(error) + strlenW(fileName) + strlenW(msgBuf) + 3) | |
| * sizeof(WCHAR)); | |
| if (fullError) | |
| { | |
| LPWSTR ptr = fullError; | |
| strcpyW(ptr, error); | |
| ptr += strlenW(error); | |
| strcpyW(ptr, fileName); | |
| ptr += strlenW(fileName); | |
| *ptr++ = ':'; | |
| *ptr++ = '\n'; | |
| strcpyW(ptr, msgBuf); | |
| MessageBoxW(hwnd, fullError, pTitle, MB_ICONERROR | MB_OK); | |
| HeapFree(GetProcessHeap(), 0, fullError); | |
| } | |
| LocalFree(msgBuf); | |
| } | |
| return ret; | |
| } | |
| static LRESULT CALLBACK import_file_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| LRESULT ret = 0; | |
| struct ImportWizData *data; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; | |
| data = (struct ImportWizData *)page->lParam; | |
| SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); | |
| if (data->fileName) | |
| { | |
| HWND fileNameEdit = GetDlgItem(hwnd, IDC_IMPORT_FILENAME); | |
| SendMessageW(fileNameEdit, WM_SETTEXT, 0, (LPARAM)data->fileName); | |
| } | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| switch (hdr->code) | |
| { | |
| case PSN_SETACTIVE: | |
| PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, | |
| PSWIZB_BACK | PSWIZB_NEXT); | |
| ret = TRUE; | |
| break; | |
| case PSN_WIZNEXT: | |
| { | |
| HWND fileNameEdit = GetDlgItem(hwnd, IDC_IMPORT_FILENAME); | |
| DWORD len = SendMessageW(fileNameEdit, WM_GETTEXTLENGTH, 0, 0); | |
| data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if (!len) | |
| { | |
| import_warning(data->dwFlags, hwnd, data->pwszWizardTitle, | |
| IDS_IMPORT_EMPTY_FILE); | |
| SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1); | |
| ret = 1; | |
| } | |
| else | |
| { | |
| LPWSTR fileName = HeapAlloc(GetProcessHeap(), 0, | |
| (len + 1) * sizeof(WCHAR)); | |
| if (fileName) | |
| { | |
| SendMessageW(fileNameEdit, WM_GETTEXT, len + 1, | |
| (LPARAM)fileName); | |
| if (!import_validate_filename(hwnd, data, fileName)) | |
| { | |
| HeapFree(GetProcessHeap(), 0, fileName); | |
| SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1); | |
| ret = 1; | |
| } | |
| else | |
| data->fileName = fileName; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| case WM_COMMAND: | |
| switch (wp) | |
| { | |
| case IDC_IMPORT_BROWSE_FILE: | |
| { | |
| OPENFILENAMEW ofn; | |
| WCHAR fileBuf[MAX_PATH]; | |
| data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| memset(&ofn, 0, sizeof(ofn)); | |
| ofn.lStructSize = sizeof(ofn); | |
| ofn.hwndOwner = hwnd; | |
| ofn.lpstrFilter = make_import_file_filter(data->dwFlags); | |
| ofn.lpstrFile = fileBuf; | |
| ofn.nMaxFile = sizeof(fileBuf) / sizeof(fileBuf[0]); | |
| fileBuf[0] = 0; | |
| if (GetOpenFileNameW(&ofn)) | |
| SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_FILENAME), WM_SETTEXT, | |
| 0, (LPARAM)ofn.lpstrFile); | |
| HeapFree(GetProcessHeap(), 0, (LPWSTR)ofn.lpstrFilter); | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| return ret; | |
| } | |
| static LRESULT CALLBACK import_store_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| LRESULT ret = 0; | |
| struct ImportWizData *data; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; | |
| data = (struct ImportWizData *)page->lParam; | |
| SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); | |
| if (!data->hDestCertStore) | |
| { | |
| SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_AUTO_STORE), BM_CLICK, | |
| 0, 0); | |
| EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_STORE), FALSE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_BROWSE_STORE), FALSE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_SPECIFY_STORE), FALSE); | |
| } | |
| else | |
| { | |
| WCHAR storeTitle[MAX_STRING_LEN]; | |
| SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_SPECIFY_STORE), BM_CLICK, | |
| 0, 0); | |
| EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_STORE), TRUE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_BROWSE_STORE), TRUE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_SPECIFY_STORE), | |
| !(data->dwFlags & CRYPTUI_WIZ_IMPORT_NO_CHANGE_DEST_STORE)); | |
| LoadStringW(hInstance, IDS_IMPORT_DEST_DETERMINED, | |
| storeTitle, sizeof(storeTitle) / sizeof(storeTitle[0])); | |
| SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_STORE), WM_SETTEXT, | |
| 0, (LPARAM)storeTitle); | |
| } | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| switch (hdr->code) | |
| { | |
| case PSN_SETACTIVE: | |
| PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, | |
| PSWIZB_BACK | PSWIZB_NEXT); | |
| ret = TRUE; | |
| break; | |
| case PSN_WIZNEXT: | |
| { | |
| data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if (IsDlgButtonChecked(hwnd, IDC_IMPORT_SPECIFY_STORE) && | |
| !data->hDestCertStore) | |
| { | |
| import_warning(data->dwFlags, hwnd, data->pwszWizardTitle, | |
| IDS_IMPORT_SELECT_STORE); | |
| SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1); | |
| ret = 1; | |
| } | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| case WM_COMMAND: | |
| switch (wp) | |
| { | |
| case IDC_IMPORT_AUTO_STORE: | |
| data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| data->autoDest = TRUE; | |
| EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_STORE), FALSE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_BROWSE_STORE), FALSE); | |
| break; | |
| case IDC_IMPORT_SPECIFY_STORE: | |
| data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| data->autoDest = FALSE; | |
| EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_STORE), TRUE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_IMPORT_BROWSE_STORE), TRUE); | |
| break; | |
| case IDC_IMPORT_BROWSE_STORE: | |
| { | |
| CRYPTUI_ENUM_SYSTEM_STORE_ARGS enumArgs = { | |
| CERT_SYSTEM_STORE_CURRENT_USER, NULL }; | |
| CRYPTUI_ENUM_DATA enumData = { 0, NULL, 1, &enumArgs }; | |
| CRYPTUI_SELECTSTORE_INFO_W selectInfo; | |
| HCERTSTORE store; | |
| data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| selectInfo.dwSize = sizeof(selectInfo); | |
| selectInfo.parent = hwnd; | |
| selectInfo.dwFlags = CRYPTUI_ENABLE_SHOW_PHYSICAL_STORE; | |
| selectInfo.pwszTitle = NULL; | |
| selectInfo.pwszText = NULL; | |
| selectInfo.pEnumData = &enumData; | |
| selectInfo.pfnSelectedStoreCallback = NULL; | |
| if ((store = CryptUIDlgSelectStoreW(&selectInfo))) | |
| { | |
| WCHAR storeTitle[MAX_STRING_LEN]; | |
| LoadStringW(hInstance, IDS_IMPORT_DEST_DETERMINED, | |
| storeTitle, sizeof(storeTitle) / sizeof(storeTitle[0])); | |
| SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_STORE), WM_SETTEXT, | |
| 0, (LPARAM)storeTitle); | |
| data->hDestCertStore = store; | |
| data->freeDest = TRUE; | |
| } | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| return ret; | |
| } | |
| static void show_import_details(HWND lv, struct ImportWizData *data) | |
| { | |
| WCHAR text[MAX_STRING_LEN]; | |
| LVITEMW item; | |
| int contentID; | |
| item.mask = LVIF_TEXT; | |
| item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0); | |
| item.iSubItem = 0; | |
| LoadStringW(hInstance, IDS_IMPORT_STORE_SELECTION, text, | |
| sizeof(text)/ sizeof(text[0])); | |
| item.pszText = text; | |
| SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item); | |
| item.iSubItem = 1; | |
| if (data->autoDest) | |
| LoadStringW(hInstance, IDS_IMPORT_DEST_AUTOMATIC, text, | |
| sizeof(text)/ sizeof(text[0])); | |
| else | |
| LoadStringW(hInstance, IDS_IMPORT_DEST_DETERMINED, text, | |
| sizeof(text)/ sizeof(text[0])); | |
| SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item); | |
| item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0); | |
| item.iSubItem = 0; | |
| LoadStringW(hInstance, IDS_IMPORT_CONTENT, text, | |
| sizeof(text)/ sizeof(text[0])); | |
| SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item); | |
| switch (data->contentType) | |
| { | |
| case CERT_QUERY_CONTENT_CERT: | |
| case CERT_QUERY_CONTENT_SERIALIZED_CERT: | |
| contentID = IDS_IMPORT_CONTENT_CERT; | |
| break; | |
| case CERT_QUERY_CONTENT_CRL: | |
| case CERT_QUERY_CONTENT_SERIALIZED_CRL: | |
| contentID = IDS_IMPORT_CONTENT_CRL; | |
| break; | |
| case CERT_QUERY_CONTENT_CTL: | |
| case CERT_QUERY_CONTENT_SERIALIZED_CTL: | |
| contentID = IDS_IMPORT_CONTENT_CTL; | |
| break; | |
| case CERT_QUERY_CONTENT_PKCS7_SIGNED: | |
| contentID = IDS_IMPORT_CONTENT_CMS; | |
| break; | |
| case CERT_QUERY_CONTENT_FLAG_PFX: | |
| contentID = IDS_IMPORT_CONTENT_PFX; | |
| break; | |
| default: | |
| contentID = IDS_IMPORT_CONTENT_STORE; | |
| break; | |
| } | |
| LoadStringW(hInstance, contentID, text, sizeof(text)/ sizeof(text[0])); | |
| item.iSubItem = 1; | |
| SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item); | |
| if (data->fileName) | |
| { | |
| item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0); | |
| item.iSubItem = 0; | |
| LoadStringW(hInstance, IDS_IMPORT_FILE, text, | |
| sizeof(text)/ sizeof(text[0])); | |
| SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item); | |
| item.iSubItem = 1; | |
| item.pszText = data->fileName; | |
| SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item); | |
| } | |
| } | |
| static BOOL do_import(DWORD dwFlags, HWND hwndParent, LPCWSTR pwszWizardTitle, | |
| PCCRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc, HCERTSTORE hDestCertStore) | |
| { | |
| BOOL ret; | |
| switch (pImportSrc->dwSubjectChoice) | |
| { | |
| case CRYPTUI_WIZ_IMPORT_SUBJECT_FILE: | |
| ret = import_file(dwFlags, hwndParent, pwszWizardTitle, | |
| pImportSrc->u.pwszFileName, hDestCertStore); | |
| break; | |
| case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_CONTEXT: | |
| if ((ret = check_context_type(dwFlags, CERT_QUERY_CONTENT_CERT))) | |
| ret = import_cert(pImportSrc->u.pCertContext, hDestCertStore); | |
| else | |
| import_warn_type_mismatch(dwFlags, hwndParent, pwszWizardTitle); | |
| break; | |
| case CRYPTUI_WIZ_IMPORT_SUBJECT_CRL_CONTEXT: | |
| if ((ret = check_context_type(dwFlags, CERT_QUERY_CONTENT_CRL))) | |
| ret = import_crl(pImportSrc->u.pCRLContext, hDestCertStore); | |
| else | |
| import_warn_type_mismatch(dwFlags, hwndParent, pwszWizardTitle); | |
| break; | |
| case CRYPTUI_WIZ_IMPORT_SUBJECT_CTL_CONTEXT: | |
| if ((ret = check_context_type(dwFlags, CERT_QUERY_CONTENT_CTL))) | |
| ret = import_ctl(pImportSrc->u.pCTLContext, hDestCertStore); | |
| else | |
| import_warn_type_mismatch(dwFlags, hwndParent, pwszWizardTitle); | |
| break; | |
| case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_STORE: | |
| ret = import_store(dwFlags, hwndParent, pwszWizardTitle, | |
| pImportSrc->u.hCertStore, hDestCertStore); | |
| break; | |
| default: | |
| WARN("unknown source type: %u\n", pImportSrc->dwSubjectChoice); | |
| SetLastError(E_INVALIDARG); | |
| ret = FALSE; | |
| } | |
| return ret; | |
| } | |
| static LRESULT CALLBACK import_finish_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| LRESULT ret = 0; | |
| struct ImportWizData *data; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; | |
| HWND lv = GetDlgItem(hwnd, IDC_IMPORT_SETTINGS); | |
| RECT rc; | |
| LVCOLUMNW column; | |
| data = (struct ImportWizData *)page->lParam; | |
| SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); | |
| SendMessageW(GetDlgItem(hwnd, IDC_IMPORT_TITLE), WM_SETFONT, | |
| (WPARAM)data->titleFont, TRUE); | |
| GetWindowRect(lv, &rc); | |
| column.mask = LVCF_WIDTH; | |
| column.cx = (rc.right - rc.left) / 2 - 2; | |
| SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column); | |
| SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column); | |
| show_import_details(lv, data); | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| switch (hdr->code) | |
| { | |
| case PSN_SETACTIVE: | |
| { | |
| HWND lv = GetDlgItem(hwnd, IDC_IMPORT_SETTINGS); | |
| data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| SendMessageW(lv, LVM_DELETEALLITEMS, 0, 0); | |
| show_import_details(lv, data); | |
| PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, | |
| PSWIZB_BACK | PSWIZB_FINISH); | |
| ret = TRUE; | |
| break; | |
| } | |
| case PSN_WIZFINISH: | |
| { | |
| data = (struct ImportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if ((data->success = do_import(data->dwFlags, hwnd, | |
| data->pwszWizardTitle, &data->importSrc, data->hDestCertStore))) | |
| { | |
| WCHAR title[MAX_STRING_LEN], message[MAX_STRING_LEN]; | |
| LPCWSTR pTitle; | |
| if (data->pwszWizardTitle) | |
| pTitle = data->pwszWizardTitle; | |
| else | |
| { | |
| LoadStringW(hInstance, IDS_IMPORT_WIZARD, title, | |
| sizeof(title) / sizeof(title[0])); | |
| pTitle = title; | |
| } | |
| LoadStringW(hInstance, IDS_IMPORT_SUCCEEDED, message, | |
| sizeof(message) / sizeof(message[0])); | |
| MessageBoxW(hwnd, message, pTitle, MB_OK); | |
| } | |
| else | |
| import_warning(data->dwFlags, hwnd, data->pwszWizardTitle, | |
| IDS_IMPORT_FAILED); | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| return ret; | |
| } | |
| static BOOL show_import_ui(DWORD dwFlags, HWND hwndParent, | |
| LPCWSTR pwszWizardTitle, PCCRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc, | |
| HCERTSTORE hDestCertStore) | |
| { | |
| PROPSHEETHEADERW hdr; | |
| PROPSHEETPAGEW pages[4]; | |
| struct ImportWizData data; | |
| int nPages = 0; | |
| data.dwFlags = dwFlags; | |
| data.pwszWizardTitle = pwszWizardTitle; | |
| if (pImportSrc) | |
| { | |
| memcpy(&data.importSrc, pImportSrc, sizeof(data.importSrc)); | |
| data.fileName = (LPWSTR)pImportSrc->u.pwszFileName; | |
| } | |
| else | |
| { | |
| memset(&data.importSrc, 0, sizeof(data.importSrc)); | |
| data.fileName = NULL; | |
| } | |
| data.freeSource = FALSE; | |
| data.hDestCertStore = hDestCertStore; | |
| data.freeDest = FALSE; | |
| data.autoDest = TRUE; | |
| data.success = TRUE; | |
| memset(&pages, 0, sizeof(pages)); | |
| pages[nPages].dwSize = sizeof(pages[0]); | |
| pages[nPages].hInstance = hInstance; | |
| pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_IMPORT_WELCOME); | |
| pages[nPages].pfnDlgProc = import_welcome_dlg_proc; | |
| pages[nPages].dwFlags = PSP_HIDEHEADER; | |
| pages[nPages].lParam = (LPARAM)&data; | |
| nPages++; | |
| if (!pImportSrc || | |
| pImportSrc->dwSubjectChoice == CRYPTUI_WIZ_IMPORT_SUBJECT_FILE) | |
| { | |
| pages[nPages].dwSize = sizeof(pages[0]); | |
| pages[nPages].hInstance = hInstance; | |
| pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_IMPORT_FILE); | |
| pages[nPages].pfnDlgProc = import_file_dlg_proc; | |
| pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; | |
| pages[nPages].pszHeaderTitle = MAKEINTRESOURCEW(IDS_IMPORT_FILE_TITLE); | |
| pages[nPages].pszHeaderSubTitle = | |
| MAKEINTRESOURCEW(IDS_IMPORT_FILE_SUBTITLE); | |
| pages[nPages].lParam = (LPARAM)&data; | |
| nPages++; | |
| } | |
| else | |
| { | |
| switch (pImportSrc->dwSubjectChoice) | |
| { | |
| case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_CONTEXT: | |
| data.contentType = CERT_QUERY_CONTENT_CERT; | |
| break; | |
| case CRYPTUI_WIZ_IMPORT_SUBJECT_CRL_CONTEXT: | |
| data.contentType = CERT_QUERY_CONTENT_CRL; | |
| break; | |
| case CRYPTUI_WIZ_IMPORT_SUBJECT_CTL_CONTEXT: | |
| data.contentType = CERT_QUERY_CONTENT_CTL; | |
| break; | |
| case CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_STORE: | |
| data.contentType = CERT_QUERY_CONTENT_SERIALIZED_STORE; | |
| break; | |
| } | |
| } | |
| pages[nPages].dwSize = sizeof(pages[0]); | |
| pages[nPages].hInstance = hInstance; | |
| pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_IMPORT_STORE); | |
| pages[nPages].pfnDlgProc = import_store_dlg_proc; | |
| pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; | |
| pages[nPages].pszHeaderTitle = MAKEINTRESOURCEW(IDS_IMPORT_STORE_TITLE); | |
| pages[nPages].pszHeaderSubTitle = | |
| MAKEINTRESOURCEW(IDS_IMPORT_STORE_SUBTITLE); | |
| pages[nPages].lParam = (LPARAM)&data; | |
| nPages++; | |
| pages[nPages].dwSize = sizeof(pages[0]); | |
| pages[nPages].hInstance = hInstance; | |
| pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_IMPORT_FINISH); | |
| pages[nPages].pfnDlgProc = import_finish_dlg_proc; | |
| pages[nPages].dwFlags = PSP_HIDEHEADER; | |
| pages[nPages].lParam = (LPARAM)&data; | |
| nPages++; | |
| memset(&hdr, 0, sizeof(hdr)); | |
| hdr.dwSize = sizeof(hdr); | |
| hdr.hwndParent = hwndParent; | |
| hdr.dwFlags = PSH_PROPSHEETPAGE | PSH_WIZARD97_NEW | PSH_HEADER | | |
| PSH_WATERMARK; | |
| hdr.hInstance = hInstance; | |
| if (pwszWizardTitle) | |
| hdr.pszCaption = pwszWizardTitle; | |
| else | |
| hdr.pszCaption = MAKEINTRESOURCEW(IDS_IMPORT_WIZARD); | |
| hdr.u3.ppsp = pages; | |
| hdr.nPages = nPages; | |
| hdr.u4.pszbmWatermark = MAKEINTRESOURCEW(IDB_CERT_WATERMARK); | |
| hdr.u5.pszbmHeader = MAKEINTRESOURCEW(IDB_CERT_HEADER); | |
| PropertySheetW(&hdr); | |
| if (data.fileName != data.importSrc.u.pwszFileName) | |
| HeapFree(GetProcessHeap(), 0, data.fileName); | |
| if (data.freeSource && | |
| data.importSrc.dwSubjectChoice == CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_STORE) | |
| CertCloseStore(data.importSrc.u.hCertStore, 0); | |
| DeleteObject(data.titleFont); | |
| return data.success; | |
| } | |
| BOOL WINAPI CryptUIWizImport(DWORD dwFlags, HWND hwndParent, LPCWSTR pwszWizardTitle, | |
| PCCRYPTUI_WIZ_IMPORT_SRC_INFO pImportSrc, HCERTSTORE hDestCertStore) | |
| { | |
| BOOL ret; | |
| TRACE("(0x%08x, %p, %s, %p, %p)\n", dwFlags, hwndParent, debugstr_w(pwszWizardTitle), | |
| pImportSrc, hDestCertStore); | |
| if (pImportSrc && | |
| pImportSrc->dwSize != sizeof(CRYPTUI_WIZ_IMPORT_SRC_INFO)) | |
| { | |
| SetLastError(E_INVALIDARG); | |
| return FALSE; | |
| } | |
| if (!(dwFlags & CRYPTUI_WIZ_NO_UI)) | |
| ret = show_import_ui(dwFlags, hwndParent, pwszWizardTitle, pImportSrc, | |
| hDestCertStore); | |
| else if (pImportSrc) | |
| ret = do_import(dwFlags, hwndParent, pwszWizardTitle, pImportSrc, | |
| hDestCertStore); | |
| else | |
| { | |
| /* Can't have no UI without specifying source */ | |
| SetLastError(E_INVALIDARG); | |
| ret = FALSE; | |
| } | |
| return ret; | |
| } | |
| struct ExportWizData | |
| { | |
| HFONT titleFont; | |
| DWORD dwFlags; | |
| LPCWSTR pwszWizardTitle; | |
| CRYPTUI_WIZ_EXPORT_INFO exportInfo; | |
| CRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO contextInfo; | |
| BOOL freePassword; | |
| PCRYPT_KEY_PROV_INFO keyProvInfo; | |
| BOOL deleteKeys; | |
| LPWSTR fileName; | |
| HANDLE file; | |
| BOOL success; | |
| }; | |
| static LRESULT CALLBACK export_welcome_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| LRESULT ret = 0; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| struct ExportWizData *data; | |
| PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; | |
| WCHAR fontFace[MAX_STRING_LEN]; | |
| HDC hDC = GetDC(hwnd); | |
| int height; | |
| data = (struct ExportWizData *)page->lParam; | |
| LoadStringW(hInstance, IDS_WIZARD_TITLE_FONT, fontFace, | |
| sizeof(fontFace) / sizeof(fontFace[0])); | |
| height = -MulDiv(12, GetDeviceCaps(hDC, LOGPIXELSY), 72); | |
| data->titleFont = CreateFontW(height, 0, 0, 0, FW_BOLD, 0, 0, 0, | |
| DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, | |
| DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, fontFace); | |
| SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_TITLE), WM_SETFONT, | |
| (WPARAM)data->titleFont, TRUE); | |
| ReleaseDC(hwnd, hDC); | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| switch (hdr->code) | |
| { | |
| case PSN_SETACTIVE: | |
| PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, PSWIZB_NEXT); | |
| ret = TRUE; | |
| break; | |
| } | |
| break; | |
| } | |
| } | |
| return ret; | |
| } | |
| static PCRYPT_KEY_PROV_INFO export_get_private_key_info(PCCERT_CONTEXT cert) | |
| { | |
| PCRYPT_KEY_PROV_INFO info = NULL; | |
| DWORD size; | |
| if (CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, | |
| NULL, &size)) | |
| { | |
| info = HeapAlloc(GetProcessHeap(), 0, size); | |
| if (info) | |
| { | |
| if (!CertGetCertificateContextProperty(cert, | |
| CERT_KEY_PROV_INFO_PROP_ID, info, &size)) | |
| { | |
| HeapFree(GetProcessHeap(), 0, info); | |
| info = NULL; | |
| } | |
| } | |
| } | |
| return info; | |
| } | |
| static BOOL export_acquire_private_key(const CRYPT_KEY_PROV_INFO *info, | |
| HCRYPTPROV *phProv) | |
| { | |
| BOOL ret; | |
| ret = CryptAcquireContextW(phProv, info->pwszContainerName, | |
| info->pwszProvName, info->dwProvType, 0); | |
| if (ret) | |
| { | |
| DWORD i; | |
| for (i = 0; i < info->cProvParam; i++) | |
| CryptSetProvParam(*phProv, info->rgProvParam[i].dwParam, | |
| info->rgProvParam[i].pbData, info->rgProvParam[i].dwFlags); | |
| } | |
| return ret; | |
| } | |
| static BOOL export_is_key_exportable(HCRYPTPROV hProv, DWORD keySpec) | |
| { | |
| BOOL ret; | |
| HCRYPTKEY key; | |
| if ((ret = CryptGetUserKey(hProv, keySpec, &key))) | |
| { | |
| DWORD permissions, size = sizeof(permissions); | |
| if ((ret = CryptGetKeyParam(key, KP_PERMISSIONS, (BYTE *)&permissions, | |
| &size, 0)) && !(permissions & CRYPT_EXPORT)) | |
| ret = FALSE; | |
| CryptDestroyKey(key); | |
| } | |
| return ret; | |
| } | |
| static LRESULT CALLBACK export_private_key_dlg_proc(HWND hwnd, UINT msg, | |
| WPARAM wp, LPARAM lp) | |
| { | |
| LRESULT ret = 0; | |
| struct ExportWizData *data; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; | |
| PCRYPT_KEY_PROV_INFO info; | |
| HCRYPTPROV hProv = 0; | |
| int errorID = 0; | |
| data = (struct ExportWizData *)page->lParam; | |
| SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); | |
| /* Get enough information about a key to see whether it's exportable. | |
| */ | |
| if (!(info = export_get_private_key_info( | |
| data->exportInfo.u.pCertContext))) | |
| errorID = IDS_EXPORT_PRIVATE_KEY_UNAVAILABLE; | |
| else if (!export_acquire_private_key(info, &hProv)) | |
| errorID = IDS_EXPORT_PRIVATE_KEY_UNAVAILABLE; | |
| else if (!export_is_key_exportable(hProv, info->dwKeySpec)) | |
| errorID = IDS_EXPORT_PRIVATE_KEY_NON_EXPORTABLE; | |
| if (errorID) | |
| { | |
| WCHAR error[MAX_STRING_LEN]; | |
| LoadStringW(hInstance, errorID, error, | |
| sizeof(error) / sizeof(error[0])); | |
| SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_PRIVATE_KEY_UNAVAILABLE), | |
| WM_SETTEXT, 0, (LPARAM)error); | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_PRIVATE_KEY_YES), FALSE); | |
| } | |
| else | |
| data->keyProvInfo = info; | |
| if (hProv) | |
| CryptReleaseContext(hProv, 0); | |
| SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_PRIVATE_KEY_NO), BM_CLICK, | |
| 0, 0); | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| switch (hdr->code) | |
| { | |
| case PSN_SETACTIVE: | |
| PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, | |
| PSWIZB_BACK | PSWIZB_NEXT); | |
| ret = TRUE; | |
| break; | |
| case PSN_WIZNEXT: | |
| data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if (IsDlgButtonChecked(hwnd, IDC_EXPORT_PRIVATE_KEY_NO)) | |
| { | |
| data->contextInfo.dwExportFormat = | |
| CRYPTUI_WIZ_EXPORT_FORMAT_DER; | |
| data->contextInfo.fExportPrivateKeys = FALSE; | |
| } | |
| else | |
| { | |
| data->contextInfo.dwExportFormat = | |
| CRYPTUI_WIZ_EXPORT_FORMAT_PFX; | |
| data->contextInfo.fExportPrivateKeys = TRUE; | |
| } | |
| break; | |
| } | |
| break; | |
| } | |
| } | |
| return ret; | |
| } | |
| static BOOL export_info_has_private_key(PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo) | |
| { | |
| BOOL ret = FALSE; | |
| if (pExportInfo->dwSubjectChoice == CRYPTUI_WIZ_EXPORT_CERT_CONTEXT) | |
| { | |
| DWORD size; | |
| /* If there's a CRYPT_KEY_PROV_INFO set for this cert, assume the | |
| * cert has a private key. | |
| */ | |
| if (CertGetCertificateContextProperty(pExportInfo->u.pCertContext, | |
| CERT_KEY_PROV_INFO_PROP_ID, NULL, &size)) | |
| ret = TRUE; | |
| } | |
| return ret; | |
| } | |
| static void export_format_enable_controls(HWND hwnd, const struct ExportWizData *data) | |
| { | |
| int defaultFormatID; | |
| switch (data->contextInfo.dwExportFormat) | |
| { | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_BASE64: | |
| defaultFormatID = IDC_EXPORT_FORMAT_BASE64; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7: | |
| defaultFormatID = IDC_EXPORT_FORMAT_CMS; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_PFX: | |
| defaultFormatID = IDC_EXPORT_FORMAT_PFX; | |
| break; | |
| default: | |
| defaultFormatID = IDC_EXPORT_FORMAT_DER; | |
| } | |
| SendMessageW(GetDlgItem(hwnd, defaultFormatID), BM_CLICK, 0, 0); | |
| if (defaultFormatID == IDC_EXPORT_FORMAT_PFX) | |
| { | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_DER), FALSE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_BASE64), FALSE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_CMS), FALSE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_PFX), TRUE); | |
| } | |
| else | |
| { | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_DER), TRUE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_BASE64), TRUE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_CMS), TRUE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_PFX), FALSE); | |
| } | |
| } | |
| static LRESULT CALLBACK export_format_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| LRESULT ret = 0; | |
| struct ExportWizData *data; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; | |
| data = (struct ExportWizData *)page->lParam; | |
| SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); | |
| export_format_enable_controls(hwnd, data); | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| switch (hdr->code) | |
| { | |
| case PSN_SETACTIVE: | |
| PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, | |
| PSWIZB_BACK | PSWIZB_NEXT); | |
| data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| export_format_enable_controls(hwnd, data); | |
| ret = TRUE; | |
| break; | |
| case PSN_WIZNEXT: | |
| { | |
| BOOL skipPasswordPage = TRUE; | |
| data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if (IsDlgButtonChecked(hwnd, IDC_EXPORT_FORMAT_DER)) | |
| data->contextInfo.dwExportFormat = | |
| CRYPTUI_WIZ_EXPORT_FORMAT_DER; | |
| else if (IsDlgButtonChecked(hwnd, IDC_EXPORT_FORMAT_BASE64)) | |
| data->contextInfo.dwExportFormat = | |
| CRYPTUI_WIZ_EXPORT_FORMAT_BASE64; | |
| else if (IsDlgButtonChecked(hwnd, IDC_EXPORT_FORMAT_CMS)) | |
| { | |
| data->contextInfo.dwExportFormat = | |
| CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7; | |
| if (IsDlgButtonChecked(hwnd, IDC_EXPORT_CMS_INCLUDE_CHAIN)) | |
| data->contextInfo.fExportChain = | |
| CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7; | |
| } | |
| else | |
| { | |
| data->contextInfo.dwExportFormat = | |
| CRYPTUI_WIZ_EXPORT_FORMAT_PFX; | |
| if (IsDlgButtonChecked(hwnd, IDC_EXPORT_PFX_INCLUDE_CHAIN)) | |
| data->contextInfo.fExportChain = TRUE; | |
| if (IsDlgButtonChecked(hwnd, IDC_EXPORT_PFX_STRONG_ENCRYPTION)) | |
| data->contextInfo.fStrongEncryption = TRUE; | |
| if (IsDlgButtonChecked(hwnd, IDC_EXPORT_PFX_DELETE_PRIVATE_KEY)) | |
| data->deleteKeys = TRUE; | |
| skipPasswordPage = FALSE; | |
| } | |
| SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, | |
| skipPasswordPage ? IDD_EXPORT_FILE : 0); | |
| ret = 1; | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| case WM_COMMAND: | |
| switch (HIWORD(wp)) | |
| { | |
| case BN_CLICKED: | |
| switch (LOWORD(wp)) | |
| { | |
| case IDC_EXPORT_FORMAT_DER: | |
| case IDC_EXPORT_FORMAT_BASE64: | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_CMS_INCLUDE_CHAIN), | |
| FALSE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_PFX_INCLUDE_CHAIN), | |
| FALSE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_PFX_STRONG_ENCRYPTION), | |
| FALSE); | |
| EnableWindow(GetDlgItem(hwnd, | |
| IDC_EXPORT_PFX_DELETE_PRIVATE_KEY), FALSE); | |
| break; | |
| case IDC_EXPORT_FORMAT_CMS: | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_CMS_INCLUDE_CHAIN), | |
| TRUE); | |
| break; | |
| case IDC_EXPORT_FORMAT_PFX: | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_PFX_INCLUDE_CHAIN), | |
| TRUE); | |
| EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_PFX_STRONG_ENCRYPTION), | |
| TRUE); | |
| EnableWindow(GetDlgItem(hwnd, | |
| IDC_EXPORT_PFX_DELETE_PRIVATE_KEY), TRUE); | |
| break; | |
| } | |
| break; | |
| } | |
| break; | |
| } | |
| return ret; | |
| } | |
| static void export_password_mismatch(HWND hwnd, const struct ExportWizData *data) | |
| { | |
| WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN]; | |
| LPCWSTR pTitle; | |
| if (data->pwszWizardTitle) | |
| pTitle = data->pwszWizardTitle; | |
| else | |
| { | |
| LoadStringW(hInstance, IDS_EXPORT_WIZARD, title, | |
| sizeof(title) / sizeof(title[0])); | |
| pTitle = title; | |
| } | |
| LoadStringW(hInstance, IDS_EXPORT_PASSWORD_MISMATCH, error, | |
| sizeof(error) / sizeof(error[0])); | |
| MessageBoxW(hwnd, error, pTitle, MB_ICONERROR | MB_OK); | |
| SetFocus(GetDlgItem(hwnd, IDC_EXPORT_PASSWORD)); | |
| } | |
| static LRESULT CALLBACK export_password_dlg_proc(HWND hwnd, UINT msg, | |
| WPARAM wp, LPARAM lp) | |
| { | |
| LRESULT ret = 0; | |
| struct ExportWizData *data; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; | |
| data = (struct ExportWizData *)page->lParam; | |
| SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| switch (hdr->code) | |
| { | |
| case PSN_SETACTIVE: | |
| PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, | |
| PSWIZB_BACK | PSWIZB_NEXT); | |
| ret = TRUE; | |
| break; | |
| case PSN_WIZNEXT: | |
| { | |
| HWND passwordEdit = GetDlgItem(hwnd, IDC_EXPORT_PASSWORD); | |
| HWND passwordConfirmEdit = GetDlgItem(hwnd, | |
| IDC_EXPORT_PASSWORD_CONFIRM); | |
| DWORD passwordLen = SendMessageW(passwordEdit, WM_GETTEXTLENGTH, | |
| 0, 0); | |
| DWORD passwordConfirmLen = SendMessageW(passwordConfirmEdit, | |
| WM_GETTEXTLENGTH, 0, 0); | |
| data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if (!passwordLen && !passwordConfirmLen) | |
| data->contextInfo.pwszPassword = NULL; | |
| else if (passwordLen != passwordConfirmLen) | |
| { | |
| export_password_mismatch(hwnd, data); | |
| SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1); | |
| ret = 1; | |
| } | |
| else | |
| { | |
| LPWSTR password = HeapAlloc(GetProcessHeap(), 0, | |
| (passwordLen + 1) * sizeof(WCHAR)); | |
| LPWSTR passwordConfirm = HeapAlloc(GetProcessHeap(), 0, | |
| (passwordConfirmLen + 1) * sizeof(WCHAR)); | |
| BOOL freePassword = TRUE; | |
| if (password && passwordConfirm) | |
| { | |
| SendMessageW(passwordEdit, WM_GETTEXT, passwordLen + 1, | |
| (LPARAM)password); | |
| SendMessageW(passwordConfirmEdit, WM_GETTEXT, | |
| passwordConfirmLen + 1, (LPARAM)passwordConfirm); | |
| if (strcmpW(password, passwordConfirm)) | |
| { | |
| export_password_mismatch(hwnd, data); | |
| SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1); | |
| ret = 1; | |
| } | |
| else | |
| { | |
| data->contextInfo.pwszPassword = password; | |
| freePassword = FALSE; | |
| data->freePassword = TRUE; | |
| } | |
| } | |
| if (freePassword) | |
| HeapFree(GetProcessHeap(), 0, password); | |
| HeapFree(GetProcessHeap(), 0, passwordConfirm); | |
| } | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| return ret; | |
| } | |
| static LPWSTR export_append_extension(const struct ExportWizData *data, | |
| LPWSTR fileName) | |
| { | |
| static const WCHAR cer[] = { '.','c','e','r',0 }; | |
| static const WCHAR crl[] = { '.','c','r','l',0 }; | |
| static const WCHAR ctl[] = { '.','c','t','l',0 }; | |
| static const WCHAR p7b[] = { '.','p','7','b',0 }; | |
| static const WCHAR pfx[] = { '.','p','f','x',0 }; | |
| static const WCHAR sst[] = { '.','s','s','t',0 }; | |
| LPCWSTR extension; | |
| LPWSTR dot; | |
| BOOL appendExtension; | |
| switch (data->contextInfo.dwExportFormat) | |
| { | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7: | |
| extension = p7b; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_PFX: | |
| extension = pfx; | |
| break; | |
| default: | |
| switch (data->exportInfo.dwSubjectChoice) | |
| { | |
| case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT: | |
| extension = crl; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT: | |
| extension = ctl; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_CERT_STORE: | |
| extension = sst; | |
| break; | |
| default: | |
| extension = cer; | |
| } | |
| } | |
| dot = strrchrW(fileName, '.'); | |
| if (dot) | |
| appendExtension = strcmpiW(dot, extension) != 0; | |
| else | |
| appendExtension = TRUE; | |
| if (appendExtension) | |
| { | |
| fileName = HeapReAlloc(GetProcessHeap(), 0, fileName, | |
| (strlenW(fileName) + strlenW(extension) + 1) * sizeof(WCHAR)); | |
| if (fileName) | |
| strcatW(fileName, extension); | |
| } | |
| return fileName; | |
| } | |
| static BOOL export_validate_filename(HWND hwnd, struct ExportWizData *data, | |
| LPCWSTR fileName) | |
| { | |
| HANDLE file; | |
| BOOL tryCreate = TRUE, forceCreate = FALSE, ret = FALSE; | |
| file = CreateFileW(fileName, GENERIC_WRITE, | |
| FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); | |
| if (file != INVALID_HANDLE_VALUE) | |
| { | |
| WCHAR warning[MAX_STRING_LEN], title[MAX_STRING_LEN]; | |
| LPCWSTR pTitle; | |
| if (data->pwszWizardTitle) | |
| pTitle = data->pwszWizardTitle; | |
| else | |
| { | |
| LoadStringW(hInstance, IDS_EXPORT_WIZARD, title, | |
| sizeof(title) / sizeof(title[0])); | |
| pTitle = title; | |
| } | |
| LoadStringW(hInstance, IDS_EXPORT_FILE_EXISTS, warning, | |
| sizeof(warning) / sizeof(warning[0])); | |
| if (MessageBoxW(hwnd, warning, pTitle, MB_YESNO) == IDYES) | |
| forceCreate = TRUE; | |
| else | |
| tryCreate = FALSE; | |
| CloseHandle(file); | |
| } | |
| if (tryCreate) | |
| { | |
| file = CreateFileW(fileName, GENERIC_WRITE, | |
| FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | |
| forceCreate ? CREATE_ALWAYS : CREATE_NEW, | |
| 0, NULL); | |
| if (file != INVALID_HANDLE_VALUE) | |
| { | |
| data->file = file; | |
| ret = TRUE; | |
| } | |
| else | |
| { | |
| WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN]; | |
| LPCWSTR pTitle; | |
| LPWSTR msgBuf, fullError; | |
| if (data->pwszWizardTitle) | |
| pTitle = data->pwszWizardTitle; | |
| else | |
| { | |
| LoadStringW(hInstance, IDS_EXPORT_WIZARD, title, | |
| sizeof(title) / sizeof(title[0])); | |
| pTitle = title; | |
| } | |
| LoadStringW(hInstance, IDS_IMPORT_OPEN_FAILED, error, | |
| sizeof(error) / sizeof(error[0])); | |
| FormatMessageW( | |
| FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, | |
| GetLastError(), 0, (LPWSTR) &msgBuf, 0, NULL); | |
| fullError = HeapAlloc(GetProcessHeap(), 0, | |
| (strlenW(error) + strlenW(fileName) + strlenW(msgBuf) + 3) | |
| * sizeof(WCHAR)); | |
| if (fullError) | |
| { | |
| LPWSTR ptr = fullError; | |
| strcpyW(ptr, error); | |
| ptr += strlenW(error); | |
| strcpyW(ptr, fileName); | |
| ptr += strlenW(fileName); | |
| *ptr++ = ':'; | |
| *ptr++ = '\n'; | |
| strcpyW(ptr, msgBuf); | |
| MessageBoxW(hwnd, fullError, pTitle, MB_ICONERROR | MB_OK); | |
| HeapFree(GetProcessHeap(), 0, fullError); | |
| } | |
| LocalFree(msgBuf); | |
| } | |
| } | |
| return ret; | |
| } | |
| static const WCHAR export_filter_cert[] = { '*','.','c','e','r',0 }; | |
| static const WCHAR export_filter_crl[] = { '*','.','c','r','l',0 }; | |
| static const WCHAR export_filter_ctl[] = { '*','.','s','t','l',0 }; | |
| static const WCHAR export_filter_cms[] = { '*','.','p','7','b',0 }; | |
| static const WCHAR export_filter_pfx[] = { '*','.','p','f','x',0 }; | |
| static const WCHAR export_filter_sst[] = { '*','.','s','s','t',0 }; | |
| static WCHAR *make_export_file_filter(DWORD exportFormat, DWORD subjectChoice) | |
| { | |
| int baseLen, allLen, totalLen = 2, baseID; | |
| LPWSTR filter = NULL, baseFilter, all; | |
| LPCWSTR filterStr; | |
| switch (exportFormat) | |
| { | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_BASE64: | |
| baseID = IDS_EXPORT_FILTER_BASE64_CERT; | |
| filterStr = export_filter_cert; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_PFX: | |
| baseID = IDS_EXPORT_FILTER_PFX; | |
| filterStr = export_filter_pfx; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7: | |
| baseID = IDS_EXPORT_FILTER_CMS; | |
| filterStr = export_filter_cms; | |
| break; | |
| default: | |
| switch (subjectChoice) | |
| { | |
| case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT: | |
| baseID = IDS_EXPORT_FILTER_CRL; | |
| filterStr = export_filter_crl; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT: | |
| baseID = IDS_EXPORT_FILTER_CTL; | |
| filterStr = export_filter_ctl; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_CERT_STORE: | |
| baseID = IDS_EXPORT_FILTER_SERIALIZED_CERT_STORE; | |
| filterStr = export_filter_sst; | |
| break; | |
| default: | |
| baseID = IDS_EXPORT_FILTER_CERT; | |
| filterStr = export_filter_cert; | |
| break; | |
| } | |
| } | |
| baseLen = LoadStringW(hInstance, baseID, (LPWSTR)&baseFilter, 0); | |
| totalLen += baseLen + strlenW(filterStr) + 2; | |
| allLen = LoadStringW(hInstance, IDS_IMPORT_FILTER_ALL, (LPWSTR)&all, 0); | |
| totalLen += allLen + strlenW(filter_all) + 2; | |
| filter = HeapAlloc(GetProcessHeap(), 0, totalLen * sizeof(WCHAR)); | |
| if (filter) | |
| { | |
| LPWSTR ptr; | |
| ptr = filter; | |
| memcpy(ptr, baseFilter, baseLen * sizeof(WCHAR)); | |
| ptr += baseLen; | |
| *ptr++ = 0; | |
| strcpyW(ptr, filterStr); | |
| ptr += strlenW(filterStr) + 1; | |
| memcpy(ptr, all, allLen * sizeof(WCHAR)); | |
| ptr += allLen; | |
| *ptr++ = 0; | |
| strcpyW(ptr, filter_all); | |
| ptr += strlenW(filter_all) + 1; | |
| *ptr++ = 0; | |
| } | |
| return filter; | |
| } | |
| static LRESULT CALLBACK export_file_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| LRESULT ret = 0; | |
| struct ExportWizData *data; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; | |
| data = (struct ExportWizData *)page->lParam; | |
| SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); | |
| if (data->exportInfo.pwszExportFileName) | |
| SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_FILENAME), WM_SETTEXT, 0, | |
| (LPARAM)data->exportInfo.pwszExportFileName); | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| switch (hdr->code) | |
| { | |
| case PSN_WIZBACK: | |
| data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if (data->contextInfo.dwExportFormat != | |
| CRYPTUI_WIZ_EXPORT_FORMAT_PFX) | |
| { | |
| SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, IDD_EXPORT_FORMAT); | |
| ret = 1; | |
| } | |
| break; | |
| case PSN_WIZNEXT: | |
| { | |
| HWND fileNameEdit = GetDlgItem(hwnd, IDC_EXPORT_FILENAME); | |
| DWORD len = SendMessageW(fileNameEdit, WM_GETTEXTLENGTH, 0, 0); | |
| data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if (!len) | |
| { | |
| WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN]; | |
| LPCWSTR pTitle; | |
| if (data->pwszWizardTitle) | |
| pTitle = data->pwszWizardTitle; | |
| else | |
| { | |
| LoadStringW(hInstance, IDS_EXPORT_WIZARD, title, | |
| sizeof(title) / sizeof(title[0])); | |
| pTitle = title; | |
| } | |
| LoadStringW(hInstance, IDS_IMPORT_EMPTY_FILE, error, | |
| sizeof(error) / sizeof(error[0])); | |
| MessageBoxW(hwnd, error, pTitle, MB_ICONERROR | MB_OK); | |
| SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1); | |
| ret = 1; | |
| } | |
| else | |
| { | |
| LPWSTR fileName = HeapAlloc(GetProcessHeap(), 0, | |
| (len + 1) * sizeof(WCHAR)); | |
| if (fileName) | |
| { | |
| SendMessageW(fileNameEdit, WM_GETTEXT, len + 1, | |
| (LPARAM)fileName); | |
| fileName = export_append_extension(data, fileName); | |
| if (!export_validate_filename(hwnd, data, fileName)) | |
| { | |
| HeapFree(GetProcessHeap(), 0, fileName); | |
| SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1); | |
| ret = 1; | |
| } | |
| else | |
| data->fileName = fileName; | |
| } | |
| } | |
| break; | |
| } | |
| case PSN_SETACTIVE: | |
| PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, | |
| PSWIZB_BACK | PSWIZB_NEXT); | |
| ret = TRUE; | |
| break; | |
| } | |
| break; | |
| } | |
| case WM_COMMAND: | |
| switch (wp) | |
| { | |
| case IDC_EXPORT_BROWSE_FILE: | |
| { | |
| OPENFILENAMEW ofn; | |
| WCHAR fileBuf[MAX_PATH]; | |
| data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| memset(&ofn, 0, sizeof(ofn)); | |
| ofn.lStructSize = sizeof(ofn); | |
| ofn.hwndOwner = hwnd; | |
| ofn.lpstrFilter = make_export_file_filter( | |
| data->contextInfo.dwExportFormat, | |
| data->exportInfo.dwSubjectChoice); | |
| ofn.lpstrFile = fileBuf; | |
| ofn.nMaxFile = sizeof(fileBuf) / sizeof(fileBuf[0]); | |
| fileBuf[0] = 0; | |
| if (GetSaveFileNameW(&ofn)) | |
| SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_FILENAME), WM_SETTEXT, | |
| 0, (LPARAM)ofn.lpstrFile); | |
| HeapFree(GetProcessHeap(), 0, (LPWSTR)ofn.lpstrFilter); | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| return ret; | |
| } | |
| static void show_export_details(HWND lv, const struct ExportWizData *data) | |
| { | |
| WCHAR text[MAX_STRING_LEN]; | |
| LVITEMW item; | |
| int contentID; | |
| item.mask = LVIF_TEXT; | |
| if (data->fileName) | |
| { | |
| item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0); | |
| item.iSubItem = 0; | |
| LoadStringW(hInstance, IDS_IMPORT_FILE, text, | |
| sizeof(text)/ sizeof(text[0])); | |
| item.pszText = text; | |
| SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item); | |
| item.iSubItem = 1; | |
| item.pszText = data->fileName; | |
| SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item); | |
| } | |
| item.pszText = text; | |
| switch (data->exportInfo.dwSubjectChoice) | |
| { | |
| case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT: | |
| case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT: | |
| case CRYPTUI_WIZ_EXPORT_CERT_STORE: | |
| case CRYPTUI_WIZ_EXPORT_CERT_STORE_CERTIFICATES_ONLY: | |
| /* do nothing */ | |
| break; | |
| default: | |
| { | |
| item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0); | |
| item.iSubItem = 0; | |
| LoadStringW(hInstance, IDS_EXPORT_INCLUDE_CHAIN, text, | |
| sizeof(text) / sizeof(text[0])); | |
| SendMessageW(lv, LVM_INSERTITEMW, item.iItem, (LPARAM)&item); | |
| item.iSubItem = 1; | |
| LoadStringW(hInstance, | |
| data->contextInfo.fExportChain ? IDS_YES : IDS_NO, text, | |
| sizeof(text) / sizeof(text[0])); | |
| SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item); | |
| item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0); | |
| item.iSubItem = 0; | |
| LoadStringW(hInstance, IDS_EXPORT_KEYS, text, | |
| sizeof(text) / sizeof(text[0])); | |
| SendMessageW(lv, LVM_INSERTITEMW, item.iItem, (LPARAM)&item); | |
| item.iSubItem = 1; | |
| LoadStringW(hInstance, | |
| data->contextInfo.fExportPrivateKeys ? IDS_YES : IDS_NO, text, | |
| sizeof(text) / sizeof(text[0])); | |
| SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item); | |
| } | |
| } | |
| item.iItem = SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0); | |
| item.iSubItem = 0; | |
| LoadStringW(hInstance, IDS_EXPORT_FORMAT, text, | |
| sizeof(text)/ sizeof(text[0])); | |
| SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item); | |
| item.iSubItem = 1; | |
| switch (data->exportInfo.dwSubjectChoice) | |
| { | |
| case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT: | |
| contentID = IDS_EXPORT_FILTER_CRL; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT: | |
| contentID = IDS_EXPORT_FILTER_CTL; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_CERT_STORE: | |
| contentID = IDS_EXPORT_FILTER_SERIALIZED_CERT_STORE; | |
| break; | |
| default: | |
| switch (data->contextInfo.dwExportFormat) | |
| { | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_BASE64: | |
| contentID = IDS_EXPORT_FILTER_BASE64_CERT; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7: | |
| contentID = IDS_EXPORT_FILTER_CMS; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_PFX: | |
| contentID = IDS_EXPORT_FILTER_PFX; | |
| break; | |
| default: | |
| contentID = IDS_EXPORT_FILTER_CERT; | |
| } | |
| } | |
| LoadStringW(hInstance, contentID, text, sizeof(text) / sizeof(text[0])); | |
| SendMessageW(lv, LVM_SETITEMTEXTW, item.iItem, (LPARAM)&item); | |
| } | |
| static inline BOOL save_der(HANDLE file, const BYTE *pb, DWORD cb) | |
| { | |
| DWORD bytesWritten; | |
| return WriteFile(file, pb, cb, &bytesWritten, NULL); | |
| } | |
| static BOOL save_base64(HANDLE file, const BYTE *pb, DWORD cb) | |
| { | |
| BOOL ret; | |
| DWORD size = 0; | |
| if ((ret = CryptBinaryToStringA(pb, cb, CRYPT_STRING_BASE64, NULL, &size))) | |
| { | |
| LPSTR buf = HeapAlloc(GetProcessHeap(), 0, size); | |
| if (buf) | |
| { | |
| if ((ret = CryptBinaryToStringA(pb, cb, CRYPT_STRING_BASE64, buf, | |
| &size))) | |
| ret = WriteFile(file, buf, size, &size, NULL); | |
| HeapFree(GetProcessHeap(), 0, buf); | |
| } | |
| else | |
| { | |
| SetLastError(ERROR_OUTOFMEMORY); | |
| ret = FALSE; | |
| } | |
| } | |
| return ret; | |
| } | |
| static inline BOOL save_store_as_cms(HANDLE file, HCERTSTORE store) | |
| { | |
| return CertSaveStore(store, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, | |
| CERT_STORE_SAVE_AS_PKCS7, CERT_STORE_SAVE_TO_FILE, file, 0); | |
| } | |
| static BOOL save_cert_as_cms(HANDLE file, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo, | |
| BOOL includeChain) | |
| { | |
| BOOL ret; | |
| HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, | |
| CERT_STORE_CREATE_NEW_FLAG, NULL); | |
| if (store) | |
| { | |
| if (includeChain) | |
| { | |
| HCERTSTORE addlStore = CertOpenStore(CERT_STORE_PROV_COLLECTION, | |
| 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); | |
| if (addlStore) | |
| { | |
| DWORD i; | |
| ret = TRUE; | |
| for (i = 0; ret && i < pExportInfo->cStores; i++) | |
| ret = CertAddStoreToCollection(addlStore, | |
| pExportInfo->rghStores, 0, 0); | |
| if (ret) | |
| { | |
| PCCERT_CHAIN_CONTEXT chain; | |
| ret = CertGetCertificateChain(NULL, | |
| pExportInfo->u.pCertContext, NULL, addlStore, NULL, 0, | |
| NULL, &chain); | |
| if (ret) | |
| { | |
| DWORD j; | |
| for (i = 0; ret && i < chain->cChain; i++) | |
| for (j = 0; ret && j < chain->rgpChain[i]->cElement; | |
| j++) | |
| ret = CertAddCertificateContextToStore(store, | |
| chain->rgpChain[i]->rgpElement[j]->pCertContext, | |
| CERT_STORE_ADD_ALWAYS, NULL); | |
| CertFreeCertificateChain(chain); | |
| } | |
| else | |
| { | |
| /* No chain could be created, just add the individual | |
| * cert to the message. | |
| */ | |
| ret = CertAddCertificateContextToStore(store, | |
| pExportInfo->u.pCertContext, CERT_STORE_ADD_ALWAYS, | |
| NULL); | |
| } | |
| } | |
| CertCloseStore(addlStore, 0); | |
| } | |
| else | |
| ret = FALSE; | |
| } | |
| else | |
| ret = CertAddCertificateContextToStore(store, | |
| pExportInfo->u.pCertContext, CERT_STORE_ADD_ALWAYS, NULL); | |
| if (ret) | |
| ret = save_store_as_cms(file, store); | |
| CertCloseStore(store, 0); | |
| } | |
| else | |
| ret = FALSE; | |
| return ret; | |
| } | |
| static BOOL save_serialized_store(HANDLE file, HCERTSTORE store) | |
| { | |
| return CertSaveStore(store, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, | |
| CERT_STORE_SAVE_AS_STORE, CERT_STORE_SAVE_TO_FILE, file, 0); | |
| } | |
| static BOOL save_pfx(HANDLE file, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo, | |
| PCCRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO pContextInfo, | |
| PCRYPT_KEY_PROV_INFO keyProvInfo, BOOL deleteKeys) | |
| { | |
| HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, | |
| 0, CERT_STORE_CREATE_NEW_FLAG, NULL); | |
| BOOL ret = FALSE; | |
| if (store) | |
| { | |
| CRYPT_DATA_BLOB pfxBlob = { 0, NULL }; | |
| PCCERT_CONTEXT cert = NULL; | |
| BOOL freeKeyProvInfo = FALSE; | |
| if (pContextInfo->fExportChain) | |
| { | |
| HCERTCHAINENGINE engine = NULL; | |
| if (pExportInfo->cStores) | |
| { | |
| CERT_CHAIN_ENGINE_CONFIG config; | |
| memset(&config, 0, sizeof(config)); | |
| config.cbSize = sizeof(config); | |
| config.cAdditionalStore = pExportInfo->cStores; | |
| config.rghAdditionalStore = pExportInfo->rghStores; | |
| ret = CertCreateCertificateChainEngine(&config, &engine); | |
| } | |
| else | |
| ret = TRUE; | |
| if (ret) | |
| { | |
| CERT_CHAIN_PARA chainPara; | |
| PCCERT_CHAIN_CONTEXT chain; | |
| memset(&chainPara, 0, sizeof(chainPara)); | |
| chainPara.cbSize = sizeof(chainPara); | |
| ret = CertGetCertificateChain(engine, | |
| pExportInfo->u.pCertContext, NULL, NULL, &chainPara, 0, NULL, | |
| &chain); | |
| if (ret) | |
| { | |
| DWORD i, j; | |
| for (i = 0; ret && i < chain->cChain; i++) | |
| for (j = 0; ret && j < chain->rgpChain[i]->cElement; | |
| j++) | |
| { | |
| if (i == 0 && j == 0) | |
| ret = CertAddCertificateContextToStore(store, | |
| chain->rgpChain[i]->rgpElement[j]->pCertContext, | |
| CERT_STORE_ADD_ALWAYS, &cert); | |
| else | |
| ret = CertAddCertificateContextToStore(store, | |
| chain->rgpChain[i]->rgpElement[j]->pCertContext, | |
| CERT_STORE_ADD_ALWAYS, NULL); | |
| } | |
| CertFreeCertificateChain(chain); | |
| } | |
| } | |
| if (engine) | |
| CertFreeCertificateChainEngine(engine); | |
| } | |
| else | |
| ret = CertAddCertificateContextToStore(store, | |
| pExportInfo->u.pCertContext, CERT_STORE_ADD_ALWAYS, &cert); | |
| /* Copy private key info to newly created cert, so it'll get exported | |
| * along with the cert. | |
| */ | |
| if (ret && pContextInfo->fExportPrivateKeys) | |
| { | |
| if (keyProvInfo) | |
| ret = CertSetCertificateContextProperty(cert, | |
| CERT_KEY_PROV_INFO_PROP_ID, 0, keyProvInfo); | |
| else | |
| { | |
| if (!(keyProvInfo = export_get_private_key_info(cert))) | |
| ret = FALSE; | |
| else | |
| { | |
| ret = CertSetCertificateContextProperty(cert, | |
| CERT_KEY_PROV_INFO_PROP_ID, 0, keyProvInfo); | |
| freeKeyProvInfo = TRUE; | |
| } | |
| } | |
| } | |
| if (ret) | |
| { | |
| DWORD exportFlags = | |
| REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY | EXPORT_PRIVATE_KEYS; | |
| ret = PFXExportCertStore(store, &pfxBlob, | |
| pContextInfo->pwszPassword, exportFlags); | |
| if (ret) | |
| { | |
| pfxBlob.pbData = HeapAlloc(GetProcessHeap(), 0, pfxBlob.cbData); | |
| if (pfxBlob.pbData) | |
| { | |
| ret = PFXExportCertStore(store, &pfxBlob, | |
| pContextInfo->pwszPassword, exportFlags); | |
| if (ret) | |
| { | |
| DWORD bytesWritten; | |
| ret = WriteFile(file, pfxBlob.pbData, pfxBlob.cbData, | |
| &bytesWritten, NULL); | |
| } | |
| } | |
| else | |
| { | |
| SetLastError(ERROR_OUTOFMEMORY); | |
| ret = FALSE; | |
| } | |
| } | |
| } | |
| if (ret && deleteKeys) | |
| { | |
| HCRYPTPROV prov; | |
| CryptAcquireContextW(&prov, keyProvInfo->pwszContainerName, | |
| keyProvInfo->pwszProvName, keyProvInfo->dwProvType, | |
| CRYPT_DELETEKEYSET); | |
| } | |
| if (freeKeyProvInfo) | |
| HeapFree(GetProcessHeap(), 0, keyProvInfo); | |
| CertFreeCertificateContext(cert); | |
| CertCloseStore(store, 0); | |
| } | |
| return ret; | |
| } | |
| static BOOL do_export(HANDLE file, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo, | |
| PCCRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO pContextInfo, | |
| PCRYPT_KEY_PROV_INFO keyProvInfo, BOOL deleteKeys) | |
| { | |
| BOOL ret; | |
| if (pContextInfo->dwSize != sizeof(CRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO)) | |
| { | |
| SetLastError(E_INVALIDARG); | |
| return FALSE; | |
| } | |
| switch (pExportInfo->dwSubjectChoice) | |
| { | |
| case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT: | |
| ret = save_der(file, | |
| pExportInfo->u.pCRLContext->pbCrlEncoded, | |
| pExportInfo->u.pCRLContext->cbCrlEncoded); | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT: | |
| ret = save_der(file, | |
| pExportInfo->u.pCTLContext->pbCtlEncoded, | |
| pExportInfo->u.pCTLContext->cbCtlEncoded); | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_CERT_STORE: | |
| ret = save_serialized_store(file, pExportInfo->u.hCertStore); | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_CERT_STORE_CERTIFICATES_ONLY: | |
| ret = save_store_as_cms(file, pExportInfo->u.hCertStore); | |
| break; | |
| default: | |
| switch (pContextInfo->dwExportFormat) | |
| { | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_DER: | |
| ret = save_der(file, pExportInfo->u.pCertContext->pbCertEncoded, | |
| pExportInfo->u.pCertContext->cbCertEncoded); | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_BASE64: | |
| ret = save_base64(file, | |
| pExportInfo->u.pCertContext->pbCertEncoded, | |
| pExportInfo->u.pCertContext->cbCertEncoded); | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7: | |
| ret = save_cert_as_cms(file, pExportInfo, | |
| pContextInfo->fExportChain); | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_FORMAT_PFX: | |
| ret = save_pfx(file, pExportInfo, pContextInfo, keyProvInfo, | |
| deleteKeys); | |
| break; | |
| default: | |
| SetLastError(E_FAIL); | |
| ret = FALSE; | |
| } | |
| } | |
| return ret; | |
| } | |
| static LRESULT CALLBACK export_finish_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, | |
| LPARAM lp) | |
| { | |
| LRESULT ret = 0; | |
| struct ExportWizData *data; | |
| switch (msg) | |
| { | |
| case WM_INITDIALOG: | |
| { | |
| PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; | |
| HWND lv = GetDlgItem(hwnd, IDC_EXPORT_SETTINGS); | |
| RECT rc; | |
| LVCOLUMNW column; | |
| data = (struct ExportWizData *)page->lParam; | |
| SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); | |
| SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_TITLE), WM_SETFONT, | |
| (WPARAM)data->titleFont, TRUE); | |
| GetWindowRect(lv, &rc); | |
| column.mask = LVCF_WIDTH; | |
| column.cx = (rc.right - rc.left) / 2 - 2; | |
| SendMessageW(lv, LVM_INSERTCOLUMNW, 0, (LPARAM)&column); | |
| SendMessageW(lv, LVM_INSERTCOLUMNW, 1, (LPARAM)&column); | |
| show_export_details(lv, data); | |
| break; | |
| } | |
| case WM_NOTIFY: | |
| { | |
| NMHDR *hdr = (NMHDR *)lp; | |
| switch (hdr->code) | |
| { | |
| case PSN_SETACTIVE: | |
| { | |
| HWND lv = GetDlgItem(hwnd, IDC_EXPORT_SETTINGS); | |
| data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| SendMessageW(lv, LVM_DELETEALLITEMS, 0, 0); | |
| show_export_details(lv, data); | |
| PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, | |
| PSWIZB_BACK | PSWIZB_FINISH); | |
| ret = TRUE; | |
| break; | |
| } | |
| case PSN_WIZFINISH: | |
| { | |
| int messageID; | |
| WCHAR title[MAX_STRING_LEN], message[MAX_STRING_LEN]; | |
| LPCWSTR pTitle; | |
| DWORD mbFlags; | |
| data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); | |
| if ((data->success = do_export(data->file, &data->exportInfo, | |
| &data->contextInfo, data->keyProvInfo, data->deleteKeys))) | |
| { | |
| messageID = IDS_EXPORT_SUCCEEDED; | |
| mbFlags = MB_OK; | |
| } | |
| else | |
| { | |
| messageID = IDS_EXPORT_FAILED; | |
| mbFlags = MB_OK | MB_ICONERROR; | |
| } | |
| if (data->pwszWizardTitle) | |
| pTitle = data->pwszWizardTitle; | |
| else | |
| { | |
| LoadStringW(hInstance, IDS_EXPORT_WIZARD, title, | |
| sizeof(title) / sizeof(title[0])); | |
| pTitle = title; | |
| } | |
| LoadStringW(hInstance, messageID, message, | |
| sizeof(message) / sizeof(message[0])); | |
| MessageBoxW(hwnd, message, pTitle, mbFlags); | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| return ret; | |
| } | |
| static BOOL show_export_ui(DWORD dwFlags, HWND hwndParent, | |
| LPCWSTR pwszWizardTitle, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo, const void *pvoid) | |
| { | |
| PROPSHEETHEADERW hdr; | |
| PROPSHEETPAGEW pages[6]; | |
| struct ExportWizData data; | |
| int nPages = 0; | |
| BOOL hasPrivateKey, showFormatPage = TRUE; | |
| INT_PTR l; | |
| data.dwFlags = dwFlags; | |
| data.pwszWizardTitle = pwszWizardTitle; | |
| memset(&data.exportInfo, 0, sizeof(data.exportInfo)); | |
| memcpy(&data.exportInfo, pExportInfo, | |
| min(sizeof(data.exportInfo), pExportInfo->dwSize)); | |
| if (pExportInfo->dwSize > sizeof(data.exportInfo)) | |
| data.exportInfo.dwSize = sizeof(data.exportInfo); | |
| data.contextInfo.dwSize = sizeof(data.contextInfo); | |
| data.contextInfo.dwExportFormat = CRYPTUI_WIZ_EXPORT_FORMAT_DER; | |
| data.contextInfo.fExportChain = FALSE; | |
| data.contextInfo.fStrongEncryption = FALSE; | |
| data.contextInfo.fExportPrivateKeys = FALSE; | |
| data.contextInfo.pwszPassword = NULL; | |
| data.freePassword = FALSE; | |
| if (pExportInfo->dwSubjectChoice == CRYPTUI_WIZ_EXPORT_CERT_CONTEXT && | |
| pvoid) | |
| memcpy(&data.contextInfo, pvoid, | |
| min(((PCCRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO)pvoid)->dwSize, | |
| sizeof(data.contextInfo))); | |
| data.keyProvInfo = NULL; | |
| data.deleteKeys = FALSE; | |
| data.fileName = NULL; | |
| data.file = INVALID_HANDLE_VALUE; | |
| data.success = FALSE; | |
| memset(&pages, 0, sizeof(pages)); | |
| pages[nPages].dwSize = sizeof(pages[0]); | |
| pages[nPages].hInstance = hInstance; | |
| pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_EXPORT_WELCOME); | |
| pages[nPages].pfnDlgProc = export_welcome_dlg_proc; | |
| pages[nPages].dwFlags = PSP_HIDEHEADER; | |
| pages[nPages].lParam = (LPARAM)&data; | |
| nPages++; | |
| hasPrivateKey = export_info_has_private_key(pExportInfo); | |
| switch (pExportInfo->dwSubjectChoice) | |
| { | |
| case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT: | |
| case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT: | |
| showFormatPage = FALSE; | |
| data.contextInfo.dwExportFormat = CRYPTUI_WIZ_EXPORT_FORMAT_DER; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_CERT_STORE: | |
| showFormatPage = FALSE; | |
| data.contextInfo.dwExportFormat = | |
| CRYPTUI_WIZ_EXPORT_FORMAT_SERIALIZED_CERT_STORE; | |
| break; | |
| case CRYPTUI_WIZ_EXPORT_CERT_STORE_CERTIFICATES_ONLY: | |
| showFormatPage = FALSE; | |
| data.contextInfo.dwExportFormat = CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7; | |
| break; | |
| } | |
| if (hasPrivateKey && showFormatPage) | |
| { | |
| pages[nPages].dwSize = sizeof(pages[0]); | |
| pages[nPages].hInstance = hInstance; | |
| pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_EXPORT_PRIVATE_KEY); | |
| pages[nPages].pfnDlgProc = export_private_key_dlg_proc; | |
| pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; | |
| pages[nPages].pszHeaderTitle = | |
| MAKEINTRESOURCEW(IDS_EXPORT_PRIVATE_KEY_TITLE); | |
| pages[nPages].pszHeaderSubTitle = | |
| MAKEINTRESOURCEW(IDS_EXPORT_PRIVATE_KEY_SUBTITLE); | |
| pages[nPages].lParam = (LPARAM)&data; | |
| nPages++; | |
| } | |
| if (showFormatPage) | |
| { | |
| pages[nPages].dwSize = sizeof(pages[0]); | |
| pages[nPages].hInstance = hInstance; | |
| pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_EXPORT_FORMAT); | |
| pages[nPages].pfnDlgProc = export_format_dlg_proc; | |
| pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; | |
| pages[nPages].pszHeaderTitle = | |
| MAKEINTRESOURCEW(IDS_EXPORT_FORMAT_TITLE); | |
| pages[nPages].pszHeaderSubTitle = | |
| MAKEINTRESOURCEW(IDS_EXPORT_FORMAT_SUBTITLE); | |
| pages[nPages].lParam = (LPARAM)&data; | |
| nPages++; | |
| } | |
| if (hasPrivateKey && showFormatPage) | |
| { | |
| pages[nPages].dwSize = sizeof(pages[0]); | |
| pages[nPages].hInstance = hInstance; | |
| pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_EXPORT_PASSWORD); | |
| pages[nPages].pfnDlgProc = export_password_dlg_proc; | |
| pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; | |
| pages[nPages].pszHeaderTitle = | |
| MAKEINTRESOURCEW(IDS_EXPORT_PASSWORD_TITLE); | |
| pages[nPages].pszHeaderSubTitle = | |
| MAKEINTRESOURCEW(IDS_EXPORT_PASSWORD_SUBTITLE); | |
| pages[nPages].lParam = (LPARAM)&data; | |
| nPages++; | |
| } | |
| pages[nPages].dwSize = sizeof(pages[0]); | |
| pages[nPages].hInstance = hInstance; | |
| pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_EXPORT_FILE); | |
| pages[nPages].pfnDlgProc = export_file_dlg_proc; | |
| pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; | |
| pages[nPages].pszHeaderTitle = MAKEINTRESOURCEW(IDS_EXPORT_FILE_TITLE); | |
| pages[nPages].pszHeaderSubTitle = | |
| MAKEINTRESOURCEW(IDS_EXPORT_FILE_SUBTITLE); | |
| pages[nPages].lParam = (LPARAM)&data; | |
| nPages++; | |
| pages[nPages].dwSize = sizeof(pages[0]); | |
| pages[nPages].hInstance = hInstance; | |
| pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_EXPORT_FINISH); | |
| pages[nPages].pfnDlgProc = export_finish_dlg_proc; | |
| pages[nPages].dwFlags = PSP_HIDEHEADER; | |
| pages[nPages].lParam = (LPARAM)&data; | |
| nPages++; | |
| memset(&hdr, 0, sizeof(hdr)); | |
| hdr.dwSize = sizeof(hdr); | |
| hdr.hwndParent = hwndParent; | |
| hdr.dwFlags = PSH_PROPSHEETPAGE | PSH_WIZARD97_NEW | PSH_HEADER | | |
| PSH_WATERMARK; | |
| hdr.hInstance = hInstance; | |
| if (pwszWizardTitle) | |
| hdr.pszCaption = pwszWizardTitle; | |
| else | |
| hdr.pszCaption = MAKEINTRESOURCEW(IDS_EXPORT_WIZARD); | |
| hdr.u3.ppsp = pages; | |
| hdr.nPages = nPages; | |
| hdr.u4.pszbmWatermark = MAKEINTRESOURCEW(IDB_CERT_WATERMARK); | |
| hdr.u5.pszbmHeader = MAKEINTRESOURCEW(IDB_CERT_HEADER); | |
| l = PropertySheetW(&hdr); | |
| DeleteObject(data.titleFont); | |
| if (data.freePassword) | |
| HeapFree(GetProcessHeap(), 0, | |
| (LPWSTR)data.contextInfo.pwszPassword); | |
| HeapFree(GetProcessHeap(), 0, data.keyProvInfo); | |
| CloseHandle(data.file); | |
| HeapFree(GetProcessHeap(), 0, data.fileName); | |
| if (l == 0) | |
| { | |
| SetLastError(ERROR_CANCELLED); | |
| return FALSE; | |
| } | |
| else | |
| return data.success; | |
| } | |
| BOOL WINAPI CryptUIWizExport(DWORD dwFlags, HWND hwndParent, | |
| LPCWSTR pwszWizardTitle, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo, void *pvoid) | |
| { | |
| BOOL ret; | |
| TRACE("(%08x, %p, %s, %p, %p)\n", dwFlags, hwndParent, | |
| debugstr_w(pwszWizardTitle), pExportInfo, pvoid); | |
| if (!(dwFlags & CRYPTUI_WIZ_NO_UI)) | |
| ret = show_export_ui(dwFlags, hwndParent, pwszWizardTitle, pExportInfo, | |
| pvoid); | |
| else | |
| { | |
| HANDLE file = CreateFileW(pExportInfo->pwszExportFileName, | |
| GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | |
| CREATE_ALWAYS, 0, NULL); | |
| if (file != INVALID_HANDLE_VALUE) | |
| { | |
| ret = do_export(file, pExportInfo, pvoid, NULL, FALSE); | |
| CloseHandle(file); | |
| } | |
| else | |
| ret = FALSE; | |
| } | |
| return ret; | |
| } |