//+---------------------------------------------------------------------------- // // File: cmstpex.cpp // // Module: CMCFG // // Synopsis: This file is the implementation of the CMSTP Extension Proc that // resides in cmcfg32.dll. This proc is used to modify the install // behavior of cmstp.exe based profile installs. // // Copyright (c) 1996-1999 Microsoft Corporation // // Author: quintinb Created 5-1-99 // // History: //+---------------------------------------------------------------------------- #include "cmmaster.h" // // For ProfileNeedsMigration // #include "needsmig.cpp" // // For GetPhoneBookPath // #include "linkdll.h" #include "linkdll.cpp" #include "allowaccess.h" #include "allowaccess.cpp" #include "getpbk.cpp" // // For GetAllUsersCmDir // #include #include "allcmdir.cpp" // // Duplicated from processcmdln.h // #include "cmstpex.h" #include "ver_str.h" #include //+---------------------------------------------------------------------------- // // Function: RenameOldCmBits // // Synopsis: This function renames all of the old CM bits so that they will not // be loaded by the system during the launch of CM after the install. // This was to prevent problems with missing entry points (either things // we had removed or added to dlls like cmutil or cmpbk32). The problem // is that cmdial32.dll is loaded explicitly from system32 by RAS (which // has a fully qualified path). However, any other dlls first check the // load directory of the exe file . . . which was cmstp.exe in the temp // dir. Thus we were getting the newest cmdial32 but older versions of // cmutil, cmpbk, etc. Thus to fix it we now rename the CM bits to .old // (cmmgr32.exe becomes cmmgr32.exe.old for instance). This forces the // loader to pick the next best place to look for dlls, the system dir. // // Arguments: LPCTSTR szTempDir -- the temp dir path where the CM bits are // // Returns: BOOL - TRUE if Successful // // History: quintinb Created 6/2/99 // //+---------------------------------------------------------------------------- BOOL RenameOldCmBits (LPCTSTR szTempDir) { // // Note that we don't rename cmstp.exe because it is doing the install. We have no need to // rename it because it is already executing and we are just trying to prevent old bits // from being loaded and executed. // // // Note that cmcfg32.dll will load the old cmutil.dll while the extension proc is running. // Please be careful when you are adding cmutil entry points to cmcfg32.dll. // BOOL bReturn = TRUE; if (szTempDir) { // // Sanity Check -- make sure we are not renaming the bits in system32 // TCHAR szTemp[MAX_PATH+1]; if (GetSystemDirectory(szTemp, MAX_PATH)) { if (0 == lstrcmpi(szTemp, szTempDir)) { return FALSE; } } TCHAR szSrc[MAX_PATH + 1]; TCHAR szDest[MAX_PATH + 1]; LPCTSTR ArrayOfCmFiles[] = { TEXT("cmmgr32.exe"), TEXT("cmpbk32.dll"), TEXT("cmdial32.dll"), TEXT("cmdl32.exe"), TEXT("cnetcfg.dll"), TEXT("cmmon32.exe"), TEXT("cmutil.dll"), TEXT("instcm.inf"), TEXT("cmcfg32.dll"), TEXT("cnet16.dll"), TEXT("ccfg95.dll"), TEXT("cmutoa.dll"), // this probably won't ever exist in an older profile but delete anyway for interim build reasons TEXT("ccfgnt.dll") }; const DWORD c_dwNumFiles = (sizeof(ArrayOfCmFiles)/sizeof(LPCTSTR)); DWORD dwGreatestNumberOfChars = lstrlen(szTempDir) + 17; // 8.3 plus one for the NULL and one to count the dot and 4 for .old if (MAX_PATH > dwGreatestNumberOfChars) { for (int i = 0; i < c_dwNumFiles; i++) { wsprintf(szSrc, TEXT("%s\\%s"), szTempDir, ArrayOfCmFiles[i]); wsprintf(szDest, TEXT("%s\\%s.old"), szTempDir, ArrayOfCmFiles[i]); if (!MoveFile(szSrc, szDest)) { DWORD dwError = GetLastError(); // // Don't report an error because a file doesn't exist. // if (ERROR_FILE_NOT_FOUND != dwError) { bReturn = FALSE; } } } } } else { bReturn = FALSE; } return bReturn; } //+---------------------------------------------------------------------------- // // Function: IsIeak5Cm // // Synopsis: This function compares the given version and build numbers against // known constants to figure out if this is an IEAK5 profile or not. // // Arguments: DWORD dwMajorAndMinorVersion -- a DWORD containing the Major Version number // in the HIWORD and the Minor Version number // in the LOWORD. // DWORD dwBuildAndQfeNumber -- a DWORD containing the Build number in the // HIWORD and the QFE number in the LOWORD. // // Returns: BOOL -- TRUE if the version numbers passed in correspond to an IEAK5 profile // // History: quintinb Created 8/2/99 // //+---------------------------------------------------------------------------- BOOL IsIeak5Cm(DWORD dwMajorAndMinorVersion, DWORD dwBuildAndQfeNumber) { BOOL bReturn = FALSE; const int c_Ieak5CmBuild = 1976; const int c_Ieak5CmMajorVer = 7; const int c_Ieak5CmMinorVer = 0; const DWORD c_dwIeak5Version = (c_Ieak5CmMajorVer << c_iShiftAmount) + c_Ieak5CmMinorVer; if ((c_dwIeak5Version == dwMajorAndMinorVersion) && (c_Ieak5CmBuild == HIWORD(dwBuildAndQfeNumber))) { bReturn = TRUE; } return bReturn; } // // RasTypeDefs // typedef DWORD (WINAPI *pfnRasSetEntryPropertiesSpec)(LPCTSTR, LPCTSTR, LPRASENTRY, DWORD, LPBYTE, DWORD); //+---------------------------------------------------------------------------- // // Function: EnumerateAndPreMigrateAllUserProfiles // // Synopsis: This function is called through the cmstp.exe extension proc. It // is used to pre-migrate 1.0 profiles. Any profile that needs migration // and hasn't been migrated yet (when the extension proc is called on an // install from an older profile), the connectoid is cleared so that the // CustomDialDll part of the connectoid entry is blanked out. This // prevents RasDeleteEntry being called on the connectoid by older versions // of cmstp.exe that don't know to clear the entry before calling it. // Otherwise, the RasCustomDeleteEntryNotify function is called and the whole // profile is deleted. This will only happen on 1.0 profiles that have // been dialed with but not migrated/upgraded. Please see NTRAID 379667 // for further details. // // Arguments: BOOL bIeak5Profile -- If the calling profile is an IEAK5 CM profile or not // // Returns: TRUE if successful // // History: quintinb Created 8/2/99 // //+---------------------------------------------------------------------------- BOOL EnumerateAndPreMigrateAllUserProfiles(BOOL bIeak5Profile) { DWORD dwValueSize; HKEY hKey; DWORD dwType; DWORD dwDataSize; TCHAR szCurrentValue[MAX_PATH+1]; TCHAR szCurrentData[MAX_PATH+1]; // // Load RasApi32.dll and get RasSetEntryProperties from it. // pfnRasSetEntryPropertiesSpec pfnSetEntryProperties = NULL; HMODULE hRasApi32 = LoadLibrary(TEXT("RASAPI32.DLL")); if (hRasApi32) { pfnSetEntryProperties = (pfnRasSetEntryPropertiesSpec)GetProcAddress(hRasApi32, "RasSetEntryPropertiesA"); if (NULL == pfnSetEntryProperties) { CMTRACE(TEXT("EnumerateAndPreMigrateAllUserProfiles -- Couldn't get RasSetEntryProperties.")); return FALSE; } } else { CMTRACE(TEXT("EnumerateAndPreMigrateAllUserProfiles -- Couldn't load rasapi32.dll.")); return FALSE; } // // Get the all user CM and all user PBK directories // TCHAR szCmAllUsersDir[MAX_PATH+1] = {0}; LPTSTR pszPhonebook = NULL; if (GetAllUsersCmDir(szCmAllUsersDir, g_hInst)) { // // TRUE is for an All-User profile // if (!GetPhoneBookPath(szCmAllUsersDir, &pszPhonebook, TRUE)) { CMTRACE(TEXT("EnumerateAndPreMigrateAllUserProfiles -- GetPhoneBookPath Failed, returning.")); return FALSE; } } else { CMTRACE(TEXT("EnumerateAndPreMigrateAllUserProfiles -- GetAllUsersCmDir Failed, returning.")); return FALSE; } // // If its and IEAK5 profile then we need to get the System Directory // TCHAR szSysDir[MAX_PATH+1]; if (bIeak5Profile) { if (0 == GetSystemDirectory(szSysDir, MAX_PATH)) { CMTRACE(TEXT("EnumerateAndPreMigrateAllUserProfiles -- GetSystemDirectory Failed, returning.")); return FALSE; } } // // Now enumerate all of the All User Profiles and see if they need any // Pre-Migration. // if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmMappings, 0, KEY_READ, &hKey)) { DWORD dwIndex = 0; dwValueSize = MAX_PATH; dwDataSize = MAX_PATH; while (ERROR_SUCCESS == RegEnumValue(hKey, dwIndex, szCurrentValue, &dwValueSize, NULL, &dwType, (LPBYTE)szCurrentData, &dwDataSize)) { if (REG_SZ == dwType) { MYDBGASSERT(0 != szCurrentValue[0]); MYDBGASSERT(0 != szCurrentData[0]); if (ProfileNeedsMigration(szCurrentValue, szCurrentData)) { // // Use GetPhoneBookPath to get the path to the phonebook. // TCHAR szCmAllUsersDir[MAX_PATH+1] = {0}; // // Use RasSetEntryProperties to clear the connectoid // RASENTRY_V500 RasEntryV5 = {0}; RasEntryV5.dwSize = sizeof(RASENTRY_V500); RasEntryV5.dwType = RASET_Internet; if (bIeak5Profile) { // // Since IEAK5 didn't migrate 1.0 connectoids // properly (it writes them in %windir%\system32\pbk\rasphone.pbk // instead of under the all users profile as appropriate), // we need to set the szCustomDialDll instead of clearing it. // wsprintf(RasEntryV5.szCustomDialDll, TEXT("%s\\cmdial32.dll"), szSysDir); } // else zero the szCustomDialDll part of the entry // RasEntryV5.szCustomDialDll[0] = TEXT('\0'); -- already zero-ed DWORD dwRet = ((pfnSetEntryProperties)(pszPhonebook, szCurrentValue, (RASENTRY*)&RasEntryV5, RasEntryV5.dwSize, NULL, 0)); if (ERROR_SUCCESS != dwRet) { CMTRACE3(TEXT("EnumerateAndPreMigrateAllUserProfiles -- RasSetEntryProperties failed on entry %s in %s, dwRet = %u"), szCurrentValue, MYDBGSTR(pszPhonebook), dwRet); } } } dwValueSize = MAX_PATH; dwDataSize = MAX_PATH; dwIndex++; } MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey)); } else { CMTRACE(TEXT("EnumerateAndPreMigrateAllUserProfiles -- No CM mappings key to migrate.")); } CmFree(pszPhonebook); return TRUE; } //+---------------------------------------------------------------------------- // // Function: RunningUnderWow64 // // Synopsis: This function is used to tell if a 32-bit process is running under // Wow64 on an ia64 machine. Note that if we are compiled 64-bit this // is always false. We make the determination by trying to call // GetSystemWow64Directory. If this function doesn't exist or returns // ERROR_CALL_NOT_IMPLEMENTED we know we are running on 32-bit. If the // function returns successfully we know we are running under wow64. // // Arguments: None // // Returns: BOOL - whether we are executing under wow64 or not // // History: quintinb Created 08/18/00 // //+---------------------------------------------------------------------------- BOOL RunningUnderWow64 () { #ifdef _WIN64 return FALSE; #else BOOL bReturn = FALSE; // // First get a module handle for kernel32.dll. Note it isn't necessary // to free this handle as GetModuleHandle doesn't change the ref count. // HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32.dll")); if (hKernel32) { // // Next get the function pointer for GetSystemWow64Directory // typedef UINT (WINAPI *pfnGetSystemWow64DirectorySpec)(LPTSTR, UINT); #ifdef UNICODE const CHAR* const c_pszGetSystemWow64FuncName = "GetSystemWow64DirectoryW"; #else const CHAR* const c_pszGetSystemWow64FuncName = "GetSystemWow64DirectoryA"; #endif pfnGetSystemWow64DirectorySpec pfnGetSystemWow64Directory = (pfnGetSystemWow64DirectorySpec)GetProcAddress(hKernel32, c_pszGetSystemWow64FuncName); if (pfnGetSystemWow64Directory) { TCHAR szSysWow64Path[MAX_PATH+1] = TEXT(""); // // GetSystemWow64Directory returns the number of chars copied to the buffer. // If we get zero back, then we need to check the last error code to see what the // reason for failure was. If it was call not implemented then we know we are // running on native x86. // UINT uReturn = pfnGetSystemWow64Directory(szSysWow64Path, MAX_PATH); DWORD dwError = GetLastError(); CMTRACE2(TEXT("RunningUnderWow64 -- GetSystemWow64Directory returned \"%s\" and %d"), szSysWow64Path, uReturn); if (uReturn) { bReturn = TRUE; } else { CMTRACE1(TEXT("RunningUnderWow64 -- GetSystemWow64Directory returned zero, checking GLE=%d"), dwError); if (ERROR_CALL_NOT_IMPLEMENTED == dwError) { bReturn = FALSE; } else { // // We got an error, the return value is indeterminant. Let's take a backup method // of looking for %windir%\syswow64 and see if we can find one. // if (GetWindowsDirectory (szSysWow64Path, MAX_PATH)) { lstrcat(szSysWow64Path, TEXT("\\syswow64")); HANDLE hDir = CreateFile(szSysWow64Path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); CMTRACE2(TEXT("RunningUnderWow64 -- Fall back algorithm. Does \"%s\" exist? %d"), szSysWow64Path, (INVALID_HANDLE_VALUE != hDir)); if (INVALID_HANDLE_VALUE != hDir) { bReturn = TRUE; CloseHandle(hDir); } } } } } } return bReturn; #endif } //+---------------------------------------------------------------------------- // // Function: CmstpExtensionProc // // Synopsis: This function is called by cmstp.exe right after it processes the command // line and again after it completes its action. It is most useful for modifying // the install behavior of profiles. Since the cmstp.exe that shipped with the profile, // not the current version of cmstp.exe is used for the install, this proc // allows us to change the install flags, change the inf path, and tell cmstp // to continue or silently fail the install. This version of the proc looks for // old versions of cmstp.exe and then copies the installation files to a temporary // directory and then launches the system's version of cmstp.exe with the new // install directory and parameters (we add the /c switch so that cmstp.exe knows to // cleanup after itself and to wait on the mutex). // // Arguments: LPDWORD pdwFlags - command line flags parsed by cmstp.exe // LPTSTR pszInfFile - Path to the original INF file // HRESULT hrRet - Return value of the action, only really used for POST // EXTENSIONDLLPROCTIMES PreOrPost - PRE or POST, tells when we are being called. // // Returns: BOOL - Whether Cmstp.exe should continue the existing install or not. // // History: quintinb Created 6/2/99 // //+---------------------------------------------------------------------------- BOOL CmstpExtensionProc(LPDWORD pdwFlags, LPTSTR pszInfFile, HRESULT hrRet, EXTENSIONDLLPROCTIMES PreOrPost) { // // We don't want 32-bit profiles installing on 64-bit. Note that the 32-bit version of cmcfg32.dll will be // in the syswow64 dir on a 64-bit machine. Thus the code below will kick in when a 32-bit version of this // function is used on a 64-bit machine. We also don't want 32-bit profiles trying to do migration, uninstalling, // etc. // if (RunningUnderWow64()) { // // If this is an install, show an error message about not being able to install 32-bit profiles // on 64-bit. // if (0 == ((*pdwFlags) & 0xFF)) { // // Get the long service name from the Inf // TCHAR szServiceName[MAX_PATH+1] = TEXT(""); TCHAR szMsg[MAX_PATH+1] = TEXT(""); MYVERIFY(0 != GetPrivateProfileString(c_pszInfSectionStrings, c_pszCmEntryServiceName, TEXT(""), szServiceName, CELEMS(szServiceName), pszInfFile)); MYVERIFY(0 != LoadString(g_hInst, IDS_NO_I386_ON_IA64, szMsg, MAX_PATH)); MYVERIFY(IDOK == MessageBox(NULL, szMsg, szServiceName, MB_OK)); } // // Fail whatever operation we were called to do // return FALSE; } // // If the first two Hex digits in the flags value are Zero, then we have an install. // Otherwise we have some other command that we wish to ignore. We also are only // interested in processing PRE install calls. // if ((0 == ((*pdwFlags) & 0xFF)) && (PRE == PreOrPost)) { CMTRACE(TEXT("CmstpExtensionProc -- Entering the cmstpex processing loop.")); // // We only wish to re-launch the install with the current cmstp.exe if we are dealing with an // older install. Thus check the version stamp in the inf file. We will re-launch any // profiles with the version number less than the current version number of cmdial32.dll. // CmVersion CmVer; DWORD dwProfileVersionNumber = (DWORD)GetPrivateProfileInt(c_pszSectionCmDial32, c_pszVersion, 0, pszInfFile); DWORD dwProfileBuildNumber = (DWORD)GetPrivateProfileInt(c_pszSectionCmDial32, c_pszVerBuild, 0, pszInfFile); if ((CmVer.GetVersionNumber() > dwProfileVersionNumber) || ((CmVer.GetVersionNumber() == dwProfileVersionNumber) && (CmVer.GetBuildAndQfeNumber() > dwProfileBuildNumber))) { // // Then we need to delete the CM bits included with the profile because // otherwise we will get install errors due to the fact that the profile // will be launched with the cmdial32.dll from system32 (the path is // explicitly specified in the connectoid for the custom dial dll), but // the load path used by the system is the directory from which the exe // module loaded (the temp dir in this case). Thus we will get a mixed // set of bits (cmdial32.dll from system32 and cmutil.dll, cmpbk32.dll, etc. // from the cab). // TCHAR szTempDir[MAX_PATH+1]; lstrcpy (szTempDir, pszInfFile); LPTSTR pszSlash = CmStrrchr(szTempDir, TEXT('\\')); if (pszSlash) { // // Then we found a last slash, Zero Terminate. // *pszSlash = TEXT('\0'); // // Now we have the old temp dir path. Lets delete the old // CM bits // MYVERIFY(0 != RenameOldCmBits (szTempDir)); } // // We also need to make sure that there aren't any 1.0 profiles that have a // 1.2 connectoid but only have a 1.0 registry format (thus they still have // a 1.0 desktop GUID interface). The problem here is that installation will try // to run profile migration on these connectoids. Older versions of cmstp.exe // would delete the existing connectoids and make new ones during profile migration. // The problem is that on NT5 we now respond to the RasCustomDeleteEntryNotify call, and // thus will uninstall profiles that have RasDeleteEntry called on their main connectoid. // To prevent this, we must pre-migrate older profiles and delete the new connectoid // properly. // EnumerateAndPreMigrateAllUserProfiles(IsIeak5Cm(dwProfileVersionNumber, dwProfileBuildNumber)); } } return TRUE; // always return TRUE so that cmstp.exe continues. Only change this if you want cmstp.exe // to fail certain actions. }