//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997-2002. // // File: CertTmpl.cpp // // Contents: Implementation of DLL Exports // //---------------------------------------------------------------------------- #include "stdafx.h" #define INITGUID #pragma warning(push,3) #include #pragma warning(pop) #include "CertTmpl_i.c" #include "about.h" // CCertTemplatesAbout #include "compdata.h" // CCertTmplSnapin #include "uuids.h" #pragma warning(push,3) #include // VER_PRODUCTVERSION_STR #include #define INCL_WINSOCK_API_TYPEDEFS 1 #include #include #include #pragma warning(pop) #include "chooser.cpp" #include "ShellExt.h" #include "PolicyOID.h" extern POLICY_OID_LIST g_policyOIDList; USE_HANDLE_MACROS ("CERTTMPL (CertTmpl.cpp)") // // This is used by the nodetype utility routines in stdutils.cpp // const struct NODETYPE_GUID_ARRAYSTRUCT g_NodetypeGuids[CERTTMPL_NUMTYPES] = { { // CERTTMPL_SNAPIN structuuidNodetypeSnapin, lstruuidNodetypeSnapin }, { // CERT_TEMPLATE structuuidNodetypeCertTemplate, lstruuidNodetypeCertTemplate } }; const struct NODETYPE_GUID_ARRAYSTRUCT* g_aNodetypeGuids = g_NodetypeGuids; const int g_cNumNodetypeGuids = CERTTMPL_NUMTYPES; const CLSID CLSID_CertTemplateShellExt = /* {11BDCE06-D55C-44e9-BC0B-8655F89E8CC5} */ { 0x11bdce06, 0xd55c, 0x44e9, { 0xbc, 0xb, 0x86, 0x55, 0xf8, 0x9e, 0x8c, 0xc5 } }; HINSTANCE g_hInstance = 0; CComModule _Module; BEGIN_OBJECT_MAP (ObjectMap) OBJECT_ENTRY (CLSID_CertTemplatesSnapin, CCertTmplSnapin) OBJECT_ENTRY (CLSID_CertTemplatesAbout, CCertTemplatesAbout) OBJECT_ENTRY(CLSID_CertTemplateShellExt, CCertTemplateShellExt) END_OBJECT_MAP () class CCertTmplApp : public CWinApp { public: CCertTmplApp (); virtual ~CCertTmplApp (); virtual BOOL InitInstance (); virtual int ExitInstance (); private: }; CCertTmplApp theApp; CCertTmplApp::CCertTmplApp () { } CCertTmplApp::~CCertTmplApp () { } BOOL CCertTmplApp::InitInstance () { #ifdef _MERGE_PROXYSTUB hProxyDll = m_hInstance; #endif g_hInstance = m_hInstance; AfxSetResourceHandle (m_hInstance); _Module.Init (ObjectMap, m_hInstance); AfxInitRichEdit(); #if DBG CheckDebugOutputLevel (); #endif SHFusionInitializeFromModuleID (m_hInstance, 2); return CWinApp::InitInstance (); } int CCertTmplApp::ExitInstance () { SHFusionUninitialize(); while ( !g_policyOIDList.IsEmpty () ) { CPolicyOID* pPolicyOID = g_policyOIDList.RemoveHead (); if ( pPolicyOID ) delete pPolicyOID; } _Module.Term (); return CWinApp::ExitInstance (); } ///////////////////////////////////////////////////////////////////////////// // Used to determine whether the DLL can be unloaded by OLE STDAPI DllCanUnloadNow (void) { AFX_MANAGE_STATE (AfxGetStaticModuleState ()); return (AfxDllCanUnloadNow ()==S_OK && _Module.GetLockCount ()==0) ? S_OK : S_FALSE; } ///////////////////////////////////////////////////////////////////////////// // Returns a class factory to create an object of the requested type STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID* ppv) { return _Module.GetClassObject (rclsid, riid, ppv); } ///////////////////////////////////////////////////////////////////////////// // DllRegisterServer - Adds entries to the system registry STDAPI DllRegisterServer (void) { AFX_MANAGE_STATE (AfxGetStaticModuleState ()); // registers object, typelib and all interfaces in typelib HRESULT hr = _Module.RegisterServer (TRUE); ASSERT (SUCCEEDED (hr)); if ( E_ACCESSDENIED == hr ) { CString caption; CString text; CThemeContextActivator activator; VERIFY (caption.LoadString (IDS_REGISTER_CERTTMPL)); VERIFY (text.LoadString (IDS_INSUFFICIENT_RIGHTS_TO_REGISTER_CERTTMPL)); MessageBox (NULL, text, caption, MB_OK); return hr; } try { CString strGUID; CString snapinName; AMC::CRegKey rkSnapins; BOOL fFound = rkSnapins.OpenKeyEx (HKEY_LOCAL_MACHINE, SNAPINS_KEY); ASSERT (fFound); if ( fFound ) { { AMC::CRegKey rkCertTmplSnapin; hr = GuidToCString (&strGUID, CLSID_CertTemplatesSnapin); if ( FAILED (hr) ) { ASSERT (FALSE); return SELFREG_E_CLASS; } rkCertTmplSnapin.CreateKeyEx (rkSnapins, strGUID); ASSERT (rkCertTmplSnapin.GetLastError () == ERROR_SUCCESS); rkCertTmplSnapin.SetString (g_szNodeType, g_aNodetypeGuids[CERTTMPL_SNAPIN].bstr); VERIFY (snapinName.LoadString (IDS_CERTTMPL_REGISTRY)); rkCertTmplSnapin.SetString (g_szNameString, (PCWSTR) snapinName); hr = GuidToCString (&strGUID, CLSID_CertTemplatesAbout); if ( FAILED (hr) ) { ASSERT (FALSE); return SELFREG_E_CLASS; } rkCertTmplSnapin.SetString (L"About", strGUID); rkCertTmplSnapin.SetString (L"Provider", L"Microsoft"); // security review 2/20/2002 BryanWal ok size_t len = strlen (VER_PRODUCTVERSION_STR); PWSTR pszVer = new WCHAR[len+1]; if ( pszVer ) { // security review BryanWal 02/20/2002 ok ::ZeroMemory (pszVer, (len+1) * sizeof (WCHAR)); // security review BryanWal 02/20/2002 cnt should be len+1 // to include conversion of NULL char len = ::mbstowcs (pszVer, VER_PRODUCTVERSION_STR, len+1); rkCertTmplSnapin.SetString (L"Version", pszVer); delete [] pszVer; } AMC::CRegKey rkCertTmplMgrStandalone; rkCertTmplMgrStandalone.CreateKeyEx (rkCertTmplSnapin, g_szStandAlone); ASSERT (rkCertTmplMgrStandalone.GetLastError () == ERROR_SUCCESS); AMC::CRegKey rkMyNodeTypes; rkMyNodeTypes.CreateKeyEx (rkCertTmplSnapin, g_szNodeTypes); ASSERT (rkMyNodeTypes.GetLastError () == ERROR_SUCCESS); AMC::CRegKey rkMyNodeType; for (int i = CERTTMPL_SNAPIN; i < CERTTMPL_NUMTYPES; i++) { rkMyNodeType.CreateKeyEx (rkMyNodeTypes, g_aNodetypeGuids[i].bstr); ASSERT (rkMyNodeType.GetLastError () == ERROR_SUCCESS); rkMyNodeType.CloseKey (); } // // BryanWal 5/18/00 // 94793: MUI: MMC: Certificates snap-in stores its display // information in the registry // // MMC now supports NameStringIndirect // // NTRAID# Bug9 611500 prefast: certtmpl: certtmpl.cpp(247) : // warning 53: Call to 'GetModuleFileNameW' may not // zero-terminate string 'achModuleFileName'. // Fix by zero'ing out the buffer and passing in 1 less than // the buffer size to GetModuleFileName WCHAR achModuleFileName[MAX_PATH+20]; ::ZeroMemory (achModuleFileName, sizeof(achModuleFileName)/sizeof(WCHAR)); if (0 != ::GetModuleFileName( AfxGetInstanceHandle(), achModuleFileName, sizeof(achModuleFileName)/sizeof(WCHAR) - 1)) { CString strNameIndirect; strNameIndirect.Format (L"@%s,-%d", achModuleFileName, IDS_CERTTMPL_REGISTRY); rkCertTmplSnapin.SetString (L"NameStringIndirect", strNameIndirect ); } rkCertTmplSnapin.CloseKey (); } AMC::CRegKey rkNodeTypes; fFound = rkNodeTypes.OpenKeyEx (HKEY_LOCAL_MACHINE, NODE_TYPES_KEY); ASSERT (fFound); if ( fFound ) { AMC::CRegKey rkNodeType; for (int i = CERTTMPL_SNAPIN; i < CERTTMPL_NUMTYPES; i++) { rkNodeType.CreateKeyEx (rkNodeTypes, g_aNodetypeGuids[i].bstr); ASSERT (rkNodeType.GetLastError () == ERROR_SUCCESS); rkNodeType.CloseKey (); } rkNodeTypes.CloseKey (); } else return SELFREG_E_CLASS; } else return SELFREG_E_CLASS; } catch (COleException* e) { ASSERT (FALSE); e->Delete (); return SELFREG_E_CLASS; } ASSERT (SUCCEEDED (hr)); return hr; } ///////////////////////////////////////////////////////////////////////////// // DllUnregisterServer - Removes entries from the system registry STDAPI DllUnregisterServer (void) { LRESULT lResult = 0; try { AMC::CRegKey rkSnapins; BOOL fFound = FALSE; do { CString strGUID; CString snapinName; fFound = rkSnapins.OpenKeyEx (HKEY_LOCAL_MACHINE, SNAPINS_KEY); ASSERT (fFound); if ( fFound ) { { AMC::CRegKey rkCertTmplSnapin; HRESULT hr = GuidToCString (&strGUID, CLSID_CertTemplatesSnapin); if ( FAILED (hr) ) { ASSERT (FALSE); lResult = SELFREG_E_CLASS; break; } lResult = RegDelnode (rkSnapins, (PCWSTR) strGUID); } AMC::CRegKey rkNodeTypes; fFound = rkNodeTypes.OpenKeyEx (HKEY_LOCAL_MACHINE, NODE_TYPES_KEY); ASSERT (fFound); if ( fFound ) { for (int i = CERTTMPL_SNAPIN; i < CERTTMPL_NUMTYPES; i++) { lResult = RegDelnode (rkNodeTypes, g_aNodetypeGuids[i].bstr); } rkNodeTypes.CloseKey (); } else { lResult = SELFREG_E_CLASS; break; } } else { lResult = SELFREG_E_CLASS; break; } } while (0); if ( fFound ) rkSnapins.CloseKey (); } catch (COleException* e) { ASSERT (FALSE); e->Delete (); lResult = SELFREG_E_CLASS; } if ( SELFREG_E_CLASS != lResult ) _Module.UnregisterServer (); return HRESULT_FROM_WIN32 (lResult); } STDAPI DllInstall(BOOL /*bInstall*/, PCWSTR /*pszCmdLine*/) { return S_OK; } /////////////////////////////////////////////////////////////////////////////// // FormatDate () // // utcDateTime (IN) - A FILETIME in UTC format. // pszDateTime (OUT) - A string containing the local date and time // formatted by locale and user preference // /////////////////////////////////////////////////////////////////////////////// HRESULT FormatDate (FILETIME utcDateTime, CString & pszDateTime) { // Time is returned as UTC, will be displayed as local. // Use FileTimeToLocalFileTime () to make it local, // then call FileTimeToSystemTime () to convert to system time, then // format with GetDateFormat () and GetTimeFormat () to display // according to user and locale preferences HRESULT hr = S_OK; FILETIME localDateTime; BOOL bResult = FileTimeToLocalFileTime (&utcDateTime, // pointer to UTC file time to convert &localDateTime); // pointer to converted file time ASSERT (bResult); if ( bResult ) { SYSTEMTIME sysTime; bResult = FileTimeToSystemTime ( &localDateTime, // pointer to file time to convert &sysTime); // pointer to structure to receive system time if ( bResult ) { CString date; CString time; // Get date // Get length to allocate buffer of sufficient size int iLen = GetDateFormat ( LOCALE_USER_DEFAULT, // locale for which date is to be formatted 0, // flags specifying function options &sysTime, // date to be formatted 0, // date format string 0, // buffer for storing formatted string 0); // size of buffer ASSERT (iLen > 0); if ( iLen > 0 ) { int iResult = GetDateFormat ( LOCALE_USER_DEFAULT, // locale for which date is to be formatted 0, // flags specifying function options &sysTime, // date to be formatted 0, // date format string date.GetBufferSetLength (iLen), // buffer for storing formatted string iLen); // size of buffer ASSERT (iResult); date.ReleaseBuffer (); if ( iResult ) pszDateTime = date; else hr = HRESULT_FROM_WIN32 (GetLastError ()); if ( iResult ) { // Get time // Get length to allocate buffer of sufficient size iLen = GetTimeFormat ( LOCALE_USER_DEFAULT, // locale for which date is to be formatted 0, // flags specifying function options &sysTime, // date to be formatted 0, // date format string 0, // buffer for storing formatted string 0); // size of buffer ASSERT (iLen > 0); if ( iLen > 0 ) { iResult = GetTimeFormat ( LOCALE_USER_DEFAULT, // locale for which date is to be formatted 0, // flags specifying function options &sysTime, // date to be formatted 0, // date format string time.GetBufferSetLength (iLen), // buffer for storing formatted string iLen); // size of buffer ASSERT (iResult); time.ReleaseBuffer (); if ( iResult ) { pszDateTime = date + L" " + time; } else hr = E_UNEXPECTED; } else hr = E_UNEXPECTED; } else hr = E_UNEXPECTED; } else { hr = HRESULT_FROM_WIN32 (GetLastError ()); } } else { hr = HRESULT_FROM_WIN32 (GetLastError ()); } } else { hr = HRESULT_FROM_WIN32 (GetLastError ()); } return hr; } void DisplaySystemError (HWND hParent, DWORD dwErr) { AFX_MANAGE_STATE (AfxGetStaticModuleState ()); LPVOID lpMsgBuf; // security review BryanWal 2/20/2002 ok because message is from system FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (PWSTR) &lpMsgBuf, 0, NULL); // Display the string. CThemeContextActivator activator; CString caption; VERIFY (caption.LoadString (IDS_CERTTMPL)); ::MessageBox (hParent, (PWSTR) lpMsgBuf, (PCWSTR) caption, MB_OK); // Free the buffer. LocalFree (lpMsgBuf); } bool IsWindowsNT() { OSVERSIONINFO versionInfo; // security review BryanWal 2/20/2002 ok ::ZeroMemory (&versionInfo, sizeof (versionInfo)); versionInfo.dwOSVersionInfoSize = sizeof (versionInfo); BOOL bResult = ::GetVersionEx (&versionInfo); ASSERT (bResult); if ( bResult ) { if ( VER_PLATFORM_WIN32_NT == versionInfo.dwPlatformId ) bResult = TRUE; } return bResult ? true : false; } ////// This stuff was stolen from windows\gina\snapins\gpedit (eric flo's stuff) ////// //************************************************************* // // RegDelnodeRecurse() // // Purpose: Deletes a registry key and all it's subkeys / values. // Called by RegDelnode // // Parameters: hKeyRoot - Root key // pwszSubKey - SubKey to delete // // Return: ERROR_SUCCESS if successful // something else if an error occurs // // Comments: // // History: Date Author Comment // 10/3/95 ericflo Created // 5/13/98 BryanWal Modified to return LRESULT // //************************************************************* LRESULT RegDelnodeRecurse (HKEY hKeyRoot, CString szSubKey) { ASSERT (hKeyRoot && !szSubKey.IsEmpty ()); if ( !hKeyRoot || szSubKey.IsEmpty () ) return ERROR_INVALID_PARAMETER; // // First, see if we can delete the key without having // to recurse. // LONG lResult = ::RegDeleteKey(hKeyRoot, szSubKey); if (lResult == ERROR_SUCCESS) { return lResult; } HKEY hKey = 0; lResult = ::RegOpenKeyEx (hKeyRoot, szSubKey, 0, KEY_READ, &hKey); if (lResult == ERROR_SUCCESS) { // ensure szSubKey ends with a slash if ( L'\\' != szSubKey.GetAt (szSubKey.GetLength () - 1) ) { szSubKey += L"\\"; } // // Enumerate the keys // DWORD dwSize = MAX_PATH; FILETIME ftWrite; WCHAR szName[MAX_PATH]; lResult = ::RegEnumKeyEx(hKey, 0, szName, &dwSize, // size in TCHARS of szName, including terminating NULL (on input) NULL, NULL, NULL, &ftWrite); if (lResult == ERROR_SUCCESS) { do { if ( ERROR_SUCCESS != RegDelnodeRecurse (hKeyRoot, szSubKey + szName) ) { break; } // // Enumerate again // dwSize = MAX_PATH; lResult = ::RegEnumKeyEx(hKey, 0, szName, &dwSize, // size in TCHARS of szName, including terminating NULL (on input) NULL, NULL, NULL, &ftWrite); } while (lResult == ERROR_SUCCESS); } ::RegCloseKey (hKey); } // remove slash from szSubKey szSubKey.Delete (szSubKey.GetLength () - 1, 1); // // Try again to delete the key // lResult = ::RegDeleteKey(hKeyRoot, szSubKey); if (lResult == ERROR_SUCCESS) { return lResult; } return lResult; } //************************************************************* // // RegDelnode() // // Purpose: Deletes a registry key and all it's subkeys / values // // Parameters: hKeyRoot - Root key // pwszSubKey - SubKey to delete // // Return: ERROR_SUCCESS if successful // something else if an error occurs // // Comments: // // History: Date Author Comment // 10/3/95 ericflo Created // 5/13/98 BryanWal Modified to return LRESULT // //************************************************************* LRESULT RegDelnode (HKEY hKeyRoot, CString szSubKey) { ASSERT (hKeyRoot && !szSubKey.IsEmpty ()); if ( !hKeyRoot || szSubKey.IsEmpty () ) return ERROR_INVALID_PARAMETER; return RegDelnodeRecurse (hKeyRoot, szSubKey); } //+-------------------------------------------------------------------------- // // Function: InitObjectPickerForDomainComputers // // Synopsis: Call IDsObjectPicker::Initialize with arguments that will // set it to allow the user to pick a single computer object. // // Arguments: [pDsObjectPicker] - object picker interface instance // // Returns: Result of calling IDsObjectPicker::Initialize. // // History: 10-14-1998 DavidMun Created // //--------------------------------------------------------------------------- HRESULT InitObjectPickerForDomainComputers(IDsObjectPicker *pDsObjectPicker) { // // Prepare to initialize the object picker. // Set up the array of scope initializer structures. // static const int SCOPE_INIT_COUNT = 1; DSOP_SCOPE_INIT_INFO aScopeInit[SCOPE_INIT_COUNT]; // security review BryanWal 02/02/2002 ok ::ZeroMemory(aScopeInit, sizeof(aScopeInit)); // // Since we just want computer objects from every scope, combine them // all in a single scope initializer. // aScopeInit[0].cbSize = sizeof(DSOP_SCOPE_INIT_INFO); aScopeInit[0].flType = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN | DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN; aScopeInit[0].FilterFlags.Uplevel.flBothModes = DSOP_FILTER_COMPUTERS; aScopeInit[0].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_COMPUTERS; // // Put the scope init array into the object picker init array // DSOP_INIT_INFO InitInfo; // security review BryanWal 02/02/2002 ok ::ZeroMemory(&InitInfo, sizeof(InitInfo)); InitInfo.cbSize = sizeof(InitInfo); InitInfo.pwzTargetComputer = NULL; // NULL == local machine InitInfo.cDsScopeInfos = SCOPE_INIT_COUNT; InitInfo.aDsScopeInfos = aScopeInit; // // Note object picker makes its own copy of InitInfo. Also note // that Initialize may be called multiple times, last call wins. // return pDsObjectPicker->Initialize(&InitInfo); } CString GetSystemMessage (DWORD dwErr) { AFX_MANAGE_STATE (AfxGetStaticModuleState ()); CString message; if ( HRESULT_FROM_WIN32 (ERROR_NO_SUCH_DOMAIN) == dwErr ) { VERIFY (message.LoadString (IDS_ERROR_NO_SUCH_DOMAIN)); } else { LPVOID lpMsgBuf = 0; // security review BryanWal 02/02/2002 ok because message is from system FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (PWSTR) &lpMsgBuf, 0, NULL ); message = (PWSTR) lpMsgBuf; // Remove white space (including new line characters) message.TrimRight (); // Free the buffer. LocalFree (lpMsgBuf); } return message; } //+--------------------------------------------------------------------------- // // Function: LocaleStrCmp // // Synopsis: Do a case insensitive string compare that is safe for any // locale. // // Arguments: [ptsz1] - strings to compare // [ptsz2] // // Returns: -1, 0, or 1 just like lstrcmpi // // History: 10-28-96 DavidMun Created // // Notes: This is slower than lstrcmpi, but will work when sorting // strings even in Japanese. // //---------------------------------------------------------------------------- int LocaleStrCmp(LPCWSTR ptsz1, LPCWSTR ptsz2) { ASSERT (ptsz1 && ptsz2); if ( !ptsz1 || !ptsz2 ) return 0; int iRet = 0; iRet = CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH, ptsz1, -1, ptsz2, -1); if (iRet) { iRet -= 2; // convert to lstrcmpi-style return -1, 0, or 1 if ( 0 == iRet ) { UNICODE_STRING unistr1; UNICODE_STRING unistr2; // security review 2/20/2002 BryanWal ok - Length is length in BYTES ::RtlInitUnicodeString (&unistr1, ptsz1); // security review 2/20/2002 BryanWal ok - Length is length in BYTES ::RtlInitUnicodeString (&unistr2, ptsz2); iRet = ::RtlCompareUnicodeString( &unistr1, &unistr2, FALSE ); } } else { DWORD dwErr = GetLastError (); _TRACE (0, L"CompareString (%s, %s) failed: 0x%x\n", ptsz1, ptsz2, dwErr); } return iRet; } void FreeStringArray (PWSTR* rgpszStrings, DWORD dwAddCount) { if ( rgpszStrings ) { for (DWORD dwIndex = 0; dwIndex < dwAddCount; dwIndex++) { if ( rgpszStrings[dwIndex] ) CoTaskMemFree (rgpszStrings[dwIndex]); } CoTaskMemFree (rgpszStrings); } } HRESULT DisplayRootNodeStatusBarText (LPCONSOLE pConsole) { if ( !pConsole ) return E_POINTER; _TRACE (1, L"Entering DisplayRootNodeStatusBarText\n"); AFX_MANAGE_STATE (AfxGetStaticModuleState ( )); CComPtr spConsole2; HRESULT hr = pConsole->QueryInterface (IID_PPV_ARG (IConsole2, &spConsole2)); if (SUCCEEDED (hr)) { CString statusText; VERIFY (statusText.LoadString (IDS_ROOTNODE_STATUSBAR_TEXT)); hr = spConsole2->SetStatusText ((PWSTR)(PCWSTR) statusText); } _TRACE (-1, L"Leaving DisplayRootNodeStatusBarText: 0x%x\n", hr); return hr; } HRESULT DisplayObjectCountInStatusBar (LPCONSOLE pConsole, DWORD dwCnt) { if ( !pConsole ) return E_POINTER; _TRACE (1, L"Entering DisplayObjectCountInStatusBar- %d, %s\n", dwCnt, L"Certificate Templates"); AFX_MANAGE_STATE (AfxGetStaticModuleState ( )); CComPtr spConsole2; HRESULT hr = pConsole->QueryInterface (IID_PPV_ARG (IConsole2, &spConsole2)); if (SUCCEEDED (hr)) { CString statusText; UINT formatID = 0; switch (dwCnt) { case -1: statusText = L""; break; case 1: VERIFY (statusText.LoadString (IDS_CERT_TEMPLATE_COUNT_SINGLE)); break; default: formatID = IDS_CERT_TEMPLATE_COUNT; break; } if ( formatID ) { // security review BryanWal 02/02/2002 ok statusText.FormatMessage (formatID, dwCnt); } hr = spConsole2->SetStatusText ((PWSTR)(PCWSTR) statusText); } _TRACE (-1, L"Leaving DisplayObjectCountInStatusBar: 0x%x\n", hr); return hr; } PCWSTR GetContextHelpFile () { static CString strHelpTopic; if ( strHelpTopic.IsEmpty () ) { UINT nLen = ::GetSystemWindowsDirectory (strHelpTopic.GetBufferSetLength(2 * MAX_PATH), 2 * MAX_PATH); strHelpTopic.ReleaseBuffer(); if (0 == nLen) { ASSERT(FALSE); return 0; } strHelpTopic += CERTTMPL_HELP_PATH; strHelpTopic += CERTTMPL_CONTEXT_HELP_FILE; } return (PCWSTR) strHelpTopic; } bool MyGetOIDInfoA (CString & string, LPCSTR pszObjId) { ASSERT (pszObjId); if ( !pszObjId ) return false; PCCRYPT_OID_INFO pOIDInfo; // This points to a constant data structure and must not be freed. bool bResult = false; // NTRAID# 479067 Certtmpl UI: Title bar and descriptive text incorrect if ( !strcmp (szOID_CERT_POLICIES, pszObjId) ) { VERIFY (string.LoadString (IDS_ISSUANCE_POLICIES)); bResult = true; } else { string = L""; pOIDInfo = ::CryptFindOIDInfo (CRYPT_OID_INFO_OID_KEY, (void *) pszObjId, 0); if ( pOIDInfo ) { string = pOIDInfo->pwszName; bResult = true; } else { for (POSITION nextPos = g_policyOIDList.GetHeadPosition (); nextPos; ) { CPolicyOID* pPolicyOID = g_policyOIDList.GetNext (nextPos); if ( pPolicyOID ) { if ( !strcmp (pPolicyOID->GetOIDA (), pszObjId) ) { string = pPolicyOID->GetDisplayName (); bResult = true; break; } } } if ( !bResult ) { // security review BryanWal 02/02/2002 ok int nLen = ::MultiByteToWideChar (CP_ACP, 0, pszObjId, -1, NULL, 0); ASSERT (nLen > 0); if ( nLen > 0) { // security review BryanWal 02/02/2002 ok // NOTICE: GetBufferSetLength () takes len not including trailing NULL, // returns NULL on failure. MultiByteToWideChar () handles NULL arg. nLen = ::MultiByteToWideChar (CP_ACP, 0, pszObjId, -1, string.GetBufferSetLength (nLen), nLen); ASSERT (nLen > 0); string.ReleaseBuffer (); } bResult = (nLen > 0) ? true : false; } } } return bResult; } #define REGSZ_ENABLE_CERTTYPE_EDITING L"EnableCertTypeEditing" bool IsCerttypeEditingAllowed() { DWORD lResult; HKEY hKey = NULL; DWORD dwType; DWORD dwEnabled = 0; DWORD cbEnabled = sizeof(dwEnabled); lResult = RegOpenKeyEx (HKEY_CURRENT_USER, L"Software\\Microsoft\\Cryptography\\CertificateTemplateCache", 0, KEY_READ, &hKey); if (lResult == ERROR_SUCCESS) { // security review BryanWal 02/02/2002 ok lResult = RegQueryValueEx(hKey, REGSZ_ENABLE_CERTTYPE_EDITING, NULL, &dwType, (PBYTE)&dwEnabled, &cbEnabled); if(lResult == ERROR_SUCCESS) { if(dwType != REG_DWORD) { dwEnabled = 0; } } RegCloseKey (hKey); } return (dwEnabled != 0); } BOOL EnumOIDInfo (PCCRYPT_OID_INFO pInfo, void* /*pvArg*/) { BOOL bRVal = TRUE; if ( pInfo && pInfo->pszOID ) { // NTRAID# 463344 Certtmpl.msc: Remove "All Application Policies" from // Extensions selection list in Certtmpl.msc -- break Enrollment if ( !strcmp (szOID_ANY_APPLICATION_POLICY, pInfo->pszOID) ) return TRUE; for (POSITION nextPos = g_policyOIDList.GetHeadPosition (); nextPos; ) { CPolicyOID* pPolicyOID = g_policyOIDList.GetNext (nextPos); if ( pPolicyOID ) { if ( !strcmp (pPolicyOID->GetOIDA (), pInfo->pszOID) ) return TRUE; // duplicate found, get next } } int flags = 0; if ( CRYPT_ENHKEY_USAGE_OID_GROUP_ID == pInfo->dwGroupId ) flags = CERT_OID_TYPE_APPLICATION_POLICY; else if ( CRYPT_POLICY_OID_GROUP_ID == pInfo->dwGroupId ) flags = CERT_OID_TYPE_ISSUER_POLICY; else { ASSERT (0); return TRUE; } CPolicyOID* pPolicyOID = new CPolicyOID (pInfo->pszOID, pInfo->pwszName, flags, false); if ( pPolicyOID ) { g_policyOIDList.AddTail (pPolicyOID); } else bRVal = FALSE; } else bRVal = FALSE; return bRVal; } HRESULT GetBuiltInOIDs () { HRESULT hr = S_OK; CryptEnumOIDInfo ( CRYPT_ENHKEY_USAGE_OID_GROUP_ID, 0, 0, EnumOIDInfo); CryptEnumOIDInfo ( CRYPT_POLICY_OID_GROUP_ID, 0, 0, EnumOIDInfo); return hr; } HRESULT EnumerateOIDs ( IDirectoryObject* pOIDContObj) { _TRACE (1, L"Entering EnumerateOIDs\n"); CComPtr spDsSearch; HRESULT hr = pOIDContObj->QueryInterface (IID_PPV_ARG(IDirectorySearch, &spDsSearch)); if ( SUCCEEDED (hr) ) { ASSERT (!!spDsSearch); ADS_SEARCHPREF_INFO pSearchPref[1]; DWORD dwNumPref = 1; pSearchPref[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE; pSearchPref[0].vValue.dwType = ADSTYPE_INTEGER; pSearchPref[0].vValue.Integer = ADS_SCOPE_ONELEVEL; hr = spDsSearch->SetSearchPreference( pSearchPref, dwNumPref ); if ( SUCCEEDED (hr) ) { static const DWORD cAttrs = 3; static PWSTR rgszAttrList[cAttrs] = {OID_PROP_DISPLAY_NAME, OID_PROP_OID, OID_PROP_TYPE}; ADS_SEARCH_HANDLE hSearchHandle = 0; wstring strQuery; ADS_SEARCH_COLUMN Column; Column.pszAttrName = 0; strQuery = L"objectClass=msPKI-Enterprise-Oid"; hr = spDsSearch->ExecuteSearch( const_cast (strQuery.c_str ()), rgszAttrList, cAttrs, &hSearchHandle ); if ( SUCCEEDED (hr) ) { while ((hr = spDsSearch->GetNextRow (hSearchHandle)) != S_ADS_NOMORE_ROWS ) { if (FAILED(hr)) continue; // // Getting current row's information // hr = spDsSearch->GetColumn( hSearchHandle, rgszAttrList[0], &Column ); if ( SUCCEEDED (hr) ) { CString strDisplayName = Column.pADsValues->CaseIgnoreString; spDsSearch->FreeColumn (&Column); Column.pszAttrName = NULL; hr = spDsSearch->GetColumn( hSearchHandle, rgszAttrList[1], &Column ); if ( SUCCEEDED (hr) ) { bool bOIDFound = false; CString strOID = Column.pADsValues->CaseIgnoreString; spDsSearch->FreeColumn (&Column); for (POSITION nextPos = g_policyOIDList.GetHeadPosition (); nextPos; ) { CPolicyOID* pPolicyOID = g_policyOIDList.GetNext (nextPos); if ( pPolicyOID ) { if ( pPolicyOID->GetOIDW () == strOID ) { bOIDFound = true; break; } } } if ( !bOIDFound ) { Column.pszAttrName = NULL; hr = spDsSearch->GetColumn( hSearchHandle, rgszAttrList[2], &Column ); if ( SUCCEEDED (hr) ) { ADS_INTEGER flags = Column.pADsValues->Integer; spDsSearch->FreeColumn (&Column); Column.pszAttrName = NULL; // Only add issuance and application OIDs to the list if ( CERT_OID_TYPE_ISSUER_POLICY == flags || CERT_OID_TYPE_APPLICATION_POLICY == flags ) { CPolicyOID* pPolicyOID = new CPolicyOID (strOID, strDisplayName, flags); if ( pPolicyOID ) { g_policyOIDList.AddTail (pPolicyOID); } else break; } } } } } else if ( hr != E_ADS_COLUMN_NOT_SET ) { break; } else { _TRACE (0, L"IDirectorySearch::GetColumn () failed: 0x%x\n", hr); } } } else { _TRACE (0, L"IDirectorySearch::ExecuteSearch () failed: 0x%x\n", hr); } spDsSearch->CloseSearchHandle(hSearchHandle); } else { _TRACE (0, L"IDirectorySearch::SetSearchPreference () failed: 0x%x\n", hr); } } else { _TRACE (0, L"IDirectoryObject::QueryInterface (IDirectorySearch) failed: 0x%x\n", hr); } _TRACE (-1, L"Leaving EnumerateOIDs: 0x%x\n", hr); return hr; } HRESULT GetEnterpriseOIDs () { _TRACE (1, L"Entering GetEnterpriseOIDs\n"); AFX_MANAGE_STATE(AfxGetStaticModuleState()); HRESULT hr = S_OK; // Empty the list first while ( !g_policyOIDList.IsEmpty () ) { CPolicyOID* pPolicyOID = g_policyOIDList.RemoveHead (); if ( pPolicyOID ) delete pPolicyOID; } hr = GetBuiltInOIDs (); if ( SUCCEEDED (hr) ) { CComPtr spPathname; // // Constructing the directory paths // // security review BryanWal 02/02/2002 ok hr = CoCreateInstance( CLSID_Pathname, NULL, CLSCTX_ALL, IID_PPV_ARG (IADsPathname, &spPathname)); if ( SUCCEEDED (hr) ) { CComBSTR bstrPathElement; ASSERT (!!spPathname); bstrPathElement = CERTTMPL_LDAP; hr = spPathname->Set(bstrPathElement, ADS_SETTYPE_PROVIDER); if ( SUCCEEDED (hr) ) { // // Open the root DSE object // bstrPathElement = CERTTMPL_ROOTDSE; hr = spPathname->AddLeafElement(bstrPathElement); if ( SUCCEEDED (hr) ) { BSTR bstrFullPath = 0; hr = spPathname->Retrieve(ADS_FORMAT_X500, &bstrFullPath); if ( SUCCEEDED (hr) ) { CComPtr spRootDSEObject; VARIANT varNamingContext; hr = ADsGetObject ( bstrFullPath, IID_PPV_ARG (IADs, &spRootDSEObject)); if ( SUCCEEDED (hr) ) { ASSERT (!!spRootDSEObject); // // Get the configuration naming context from the root DSE // bstrPathElement = CERTTMPL_CONFIG_NAMING_CONTEXT; hr = spRootDSEObject->Get(bstrPathElement, &varNamingContext); if ( SUCCEEDED (hr) ) { hr = spPathname->Set(V_BSTR(&varNamingContext), ADS_SETTYPE_DN); if ( SUCCEEDED (hr) ) { bstrPathElement = L"CN=Services"; hr = spPathname->AddLeafElement (bstrPathElement); if ( SUCCEEDED (hr) ) { bstrPathElement = L"CN=Public Key Services"; hr = spPathname->AddLeafElement (bstrPathElement); if ( SUCCEEDED (hr) ) { bstrPathElement = L"CN=OID"; hr = spPathname->AddLeafElement (bstrPathElement); if ( SUCCEEDED (hr) ) { BSTR bstrOIDPath = 0; hr = spPathname->Retrieve(ADS_FORMAT_X500, &bstrOIDPath); if ( SUCCEEDED (hr) ) { CComPtr spOIDContObj; hr = ADsGetObject ( bstrOIDPath, IID_PPV_ARG (IDirectoryObject, &spOIDContObj)); if ( SUCCEEDED (hr) ) { hr = EnumerateOIDs (spOIDContObj); } else { _TRACE (0, L"ADsGetObject (%s) failed: 0x%x\n", bstrOIDPath, hr); } SysFreeString (bstrOIDPath); } } } } } } else { _TRACE (0, L"IADs::Get (%s) failed: 0x%x\n", CERTTMPL_CONFIG_NAMING_CONTEXT, hr); } } else { _TRACE (0, L"ADsGetObject (%s) failed: 0x%x\n", bstrFullPath, hr); } } } } } else hr = E_POINTER; } _TRACE (-1, L"Leaving GetEnterpriseOIDs: 0x%x\n", hr); return hr; } bool OIDHasValidFormat (PCWSTR pszOidValue, int& rErrorTypeStrID) { ASSERT (pszOidValue); if ( !pszOidValue ) return false; _TRACE (1, L"Entering OIDHasValidFormat (%s)\n", pszOidValue); rErrorTypeStrID = 0; bool bFormatIsValid = false; int nLen = WideCharToMultiByte( CP_ACP, // code page 0, // performance and mapping flags pszOidValue, // wide-character string -1, // -1 - calculate length of null-terminated string automatically 0, // buffer for new string 0, // size of buffer - 0 causes API to return length including null terminator 0, // default for unmappable chars 0); // set when default char used if ( nLen > 0 ) { PSTR pszAnsiBuf = new char[nLen]; if ( pszAnsiBuf ) { // security review BryanWal 02/02/2002 ok ZeroMemory (pszAnsiBuf, nLen*sizeof(char)); // security review BryanWal 02/02/2002 ok nLen = WideCharToMultiByte( CP_ACP, // code page 0, // performance and mapping flags pszOidValue, // wide-character string -1, // -1 - calculate length of null-terminated string automatically pszAnsiBuf, // buffer for new string nLen, // size of buffer 0, // default for unmappable chars 0); // set when default char used if ( nLen ) { // According to PhilH: // The first number is limited to // 0,1 or 2. The second number is // limited to 0 - 39 when the first // number is 0 or 1. Otherwise, any // number. // Also, according to X.208, there // must be at least 2 numbers. bFormatIsValid = true; // security review 2/20/2002 BryanWal ok size_t cbAnsiBufLen = strlen (pszAnsiBuf); // check for only digits and "." size_t nIdx = strspn (pszAnsiBuf, "0123456789.\0"); if ( nIdx > 0 && nIdx < cbAnsiBufLen ) { bFormatIsValid = false; rErrorTypeStrID = IDS_OID_CONTAINS_NON_DIGITS; } // check for consecutive "."s - string not valid if present if ( bFormatIsValid && strstr (pszAnsiBuf, "..") ) { bFormatIsValid = false; rErrorTypeStrID = IDS_OID_CONTAINS_CONSECUTIVE_DOTS; } // must begin with "0." or "1." or "2." bool bFirstNumberIs0 = false; bool bFirstNumberIs1 = false; bool bFirstNumberIs2 = false; if ( bFormatIsValid ) { if ( !strncmp (pszAnsiBuf, "0.", 2) ) bFirstNumberIs0 = true; else if ( !strncmp (pszAnsiBuf, "1.", 2) ) bFirstNumberIs1 = true; else if ( !strncmp (pszAnsiBuf, "2.", 2) ) bFirstNumberIs2 = true; if ( !bFirstNumberIs0 && !bFirstNumberIs1 && !bFirstNumberIs2 ) { bFormatIsValid = false; rErrorTypeStrID = IDS_OID_MUST_START_WITH_0_1_2; } } if ( bFormatIsValid && ( bFirstNumberIs0 || bFirstNumberIs1 ) ) { PSTR pszBuf = pszAnsiBuf; pszBuf += 2; // there must be a number after the dot // security review 2/20/2002 BryanWal ok if ( strlen (pszBuf) ) { // truncate the string at the next dot, if any PSTR pszDot = strstr (pszBuf, "."); if ( pszDot ) pszDot[0] = 0; // convert the string to a number and check for range 0-39 int nValue = atoi (pszBuf); if ( nValue < 0 || nValue > 39 ) { bFormatIsValid = false; rErrorTypeStrID = IDS_OID_0_1_MUST_BE_0_TO_39; } } else { bFormatIsValid = false; rErrorTypeStrID = IDS_OID_MUST_HAVE_TWO_NUMBERS; } } // ensure no trailing "." if ( bFormatIsValid ) { if ( '.' == pszAnsiBuf[cbAnsiBufLen - 1] ) { bFormatIsValid = false; rErrorTypeStrID = IDS_OID_CANNOT_END_WITH_DOT; } } if ( bFormatIsValid ) { bFormatIsValid = false; CRYPT_ATTRIBUTE cryptAttr; // security review BryanWal 02/02/2002 ok ::ZeroMemory (&cryptAttr, sizeof (cryptAttr)); cryptAttr.cValue = 0; cryptAttr.pszObjId = pszAnsiBuf; cryptAttr.rgValue = 0; DWORD cbEncoded = 0; BOOL bResult = CryptEncodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_ATTRIBUTE, &cryptAttr, NULL, &cbEncoded); if ( cbEncoded > 0 ) { BYTE* pBuffer = new BYTE[cbEncoded]; if ( pBuffer ) { bResult = CryptEncodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_ATTRIBUTE, &cryptAttr, pBuffer, &cbEncoded); if ( bResult ) { DWORD cbStructInfo = 0; bResult = CryptDecodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_ATTRIBUTE, pBuffer, cbEncoded, 0, 0, &cbStructInfo); if ( cbStructInfo > 0 ) { BYTE* pStructBuf = new BYTE[cbStructInfo]; if ( pStructBuf ) { bResult = CryptDecodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_ATTRIBUTE, pBuffer, cbEncoded, 0, pStructBuf, &cbStructInfo); if ( bResult ) { CRYPT_ATTRIBUTE* pCryptAttr = (CRYPT_ATTRIBUTE*) pStructBuf; if ( !strcmp (pszAnsiBuf, pCryptAttr->pszObjId) ) { bFormatIsValid = true; } } delete [] pStructBuf; } } } delete [] pBuffer; } } } } else { _TRACE (0, L"WideCharToMultiByte (%s) failed: 0x%x\n", pszOidValue, GetLastError ()); } delete [] pszAnsiBuf; } } else { _TRACE (0, L"WideCharToMultiByte (%s) failed: 0x%x\n", pszOidValue, GetLastError ()); } _TRACE (-1, L"Leaving EnumerateOIDs: %s\n", bFormatIsValid ? L"true" : L"false"); return bFormatIsValid; } HPROPSHEETPAGE MyCreatePropertySheetPage(AFX_OLDPROPSHEETPAGE* psp) { ASSERT (psp); if ( !psp ) return 0; PROPSHEETPAGE_V3 sp_v3 = {0}; // security review 2/20/2002 BryanWal ok ASSERT (sizeof (sp_v3) >= psp->dwSize); if ( sizeof (sp_v3) >= psp->dwSize ) { CopyMemory (&sp_v3, psp, psp->dwSize); sp_v3.dwSize = sizeof(sp_v3); } else return 0; return (::CreatePropertySheetPage (&sp_v3)); }