Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

592 lines
23 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // File: cmstpex.cpp
  4. //
  5. // Module: CMCFG
  6. //
  7. // Synopsis: This file is the implementation of the CMSTP Extension Proc that
  8. // resides in cmcfg32.dll. This proc is used to modify the install
  9. // behavior of cmstp.exe based profile installs.
  10. //
  11. // Copyright (c) 1996-1999 Microsoft Corporation
  12. //
  13. // Author: quintinb Created 5-1-99
  14. //
  15. // History:
  16. //+----------------------------------------------------------------------------
  17. #include "cmmaster.h"
  18. //
  19. // For ProfileNeedsMigration
  20. //
  21. #include "needsmig.cpp"
  22. //
  23. // For GetPhoneBookPath
  24. //
  25. #include "linkdll.h"
  26. #include "linkdll.cpp"
  27. #include "allowaccess.h"
  28. #include "allowaccess.cpp"
  29. #include "getpbk.cpp"
  30. //
  31. // For GetAllUsersCmDir
  32. //
  33. #include <shlobj.h>
  34. #include "allcmdir.cpp"
  35. //
  36. // Duplicated from processcmdln.h
  37. //
  38. #include "cmstpex.h"
  39. #include "ver_str.h"
  40. #include <shellapi.h>
  41. //+----------------------------------------------------------------------------
  42. //
  43. // Function: RenameOldCmBits
  44. //
  45. // Synopsis: This function renames all of the old CM bits so that they will not
  46. // be loaded by the system during the launch of CM after the install.
  47. // This was to prevent problems with missing entry points (either things
  48. // we had removed or added to dlls like cmutil or cmpbk32). The problem
  49. // is that cmdial32.dll is loaded explicitly from system32 by RAS (which
  50. // has a fully qualified path). However, any other dlls first check the
  51. // load directory of the exe file . . . which was cmstp.exe in the temp
  52. // dir. Thus we were getting the newest cmdial32 but older versions of
  53. // cmutil, cmpbk, etc. Thus to fix it we now rename the CM bits to .old
  54. // (cmmgr32.exe becomes cmmgr32.exe.old for instance). This forces the
  55. // loader to pick the next best place to look for dlls, the system dir.
  56. //
  57. // Arguments: LPCTSTR szTempDir -- the temp dir path where the CM bits are
  58. //
  59. // Returns: BOOL - TRUE if Successful
  60. //
  61. // History: quintinb Created 6/2/99
  62. //
  63. //+----------------------------------------------------------------------------
  64. BOOL RenameOldCmBits (LPCTSTR szTempDir)
  65. {
  66. //
  67. // Note that we don't rename cmstp.exe because it is doing the install. We have no need to
  68. // rename it because it is already executing and we are just trying to prevent old bits
  69. // from being loaded and executed.
  70. //
  71. //
  72. // Note that cmcfg32.dll will load the old cmutil.dll while the extension proc is running.
  73. // Please be careful when you are adding cmutil entry points to cmcfg32.dll.
  74. //
  75. BOOL bReturn = TRUE;
  76. if (szTempDir)
  77. {
  78. //
  79. // Sanity Check -- make sure we are not renaming the bits in system32
  80. //
  81. TCHAR szTemp[MAX_PATH+1];
  82. if (GetSystemDirectory(szTemp, MAX_PATH))
  83. {
  84. if (0 == lstrcmpi(szTemp, szTempDir))
  85. {
  86. return FALSE;
  87. }
  88. }
  89. TCHAR szSrc[MAX_PATH + 1];
  90. TCHAR szDest[MAX_PATH + 1];
  91. LPCTSTR ArrayOfCmFiles[] =
  92. {
  93. TEXT("cmmgr32.exe"),
  94. TEXT("cmpbk32.dll"),
  95. TEXT("cmdial32.dll"),
  96. TEXT("cmdl32.exe"),
  97. TEXT("cnetcfg.dll"),
  98. TEXT("cmmon32.exe"),
  99. TEXT("cmutil.dll"),
  100. TEXT("instcm.inf"),
  101. TEXT("cmcfg32.dll"),
  102. TEXT("cnet16.dll"),
  103. TEXT("ccfg95.dll"),
  104. TEXT("cmutoa.dll"), // this probably won't ever exist in an older profile but delete anyway for interim build reasons
  105. TEXT("ccfgnt.dll")
  106. };
  107. const DWORD c_dwNumFiles = (sizeof(ArrayOfCmFiles)/sizeof(LPCTSTR));
  108. DWORD dwGreatestNumberOfChars = lstrlen(szTempDir) + 17; // 8.3 plus one for the NULL and one to count the dot and 4 for .old
  109. if (MAX_PATH > dwGreatestNumberOfChars)
  110. {
  111. for (int i = 0; i < c_dwNumFiles; i++)
  112. {
  113. wsprintf(szSrc, TEXT("%s\\%s"), szTempDir, ArrayOfCmFiles[i]);
  114. wsprintf(szDest, TEXT("%s\\%s.old"), szTempDir, ArrayOfCmFiles[i]);
  115. if (!MoveFile(szSrc, szDest))
  116. {
  117. DWORD dwError = GetLastError();
  118. //
  119. // Don't report an error because a file doesn't exist.
  120. //
  121. if (ERROR_FILE_NOT_FOUND != dwError)
  122. {
  123. bReturn = FALSE;
  124. }
  125. }
  126. }
  127. }
  128. }
  129. else
  130. {
  131. bReturn = FALSE;
  132. }
  133. return bReturn;
  134. }
  135. //+----------------------------------------------------------------------------
  136. //
  137. // Function: IsIeak5Cm
  138. //
  139. // Synopsis: This function compares the given version and build numbers against
  140. // known constants to figure out if this is an IEAK5 profile or not.
  141. //
  142. // Arguments: DWORD dwMajorAndMinorVersion -- a DWORD containing the Major Version number
  143. // in the HIWORD and the Minor Version number
  144. // in the LOWORD.
  145. // DWORD dwBuildAndQfeNumber -- a DWORD containing the Build number in the
  146. // HIWORD and the QFE number in the LOWORD.
  147. //
  148. // Returns: BOOL -- TRUE if the version numbers passed in correspond to an IEAK5 profile
  149. //
  150. // History: quintinb Created 8/2/99
  151. //
  152. //+----------------------------------------------------------------------------
  153. BOOL IsIeak5Cm(DWORD dwMajorAndMinorVersion, DWORD dwBuildAndQfeNumber)
  154. {
  155. BOOL bReturn = FALSE;
  156. const int c_Ieak5CmBuild = 1976;
  157. const int c_Ieak5CmMajorVer = 7;
  158. const int c_Ieak5CmMinorVer = 0;
  159. const DWORD c_dwIeak5Version = (c_Ieak5CmMajorVer << c_iShiftAmount) + c_Ieak5CmMinorVer;
  160. if ((c_dwIeak5Version == dwMajorAndMinorVersion) &&
  161. (c_Ieak5CmBuild == HIWORD(dwBuildAndQfeNumber)))
  162. {
  163. bReturn = TRUE;
  164. }
  165. return bReturn;
  166. }
  167. //
  168. // RasTypeDefs
  169. //
  170. typedef DWORD (WINAPI *pfnRasSetEntryPropertiesSpec)(LPCTSTR, LPCTSTR, LPRASENTRY, DWORD, LPBYTE, DWORD);
  171. //+----------------------------------------------------------------------------
  172. //
  173. // Function: EnumerateAndPreMigrateAllUserProfiles
  174. //
  175. // Synopsis: This function is called through the cmstp.exe extension proc. It
  176. // is used to pre-migrate 1.0 profiles. Any profile that needs migration
  177. // and hasn't been migrated yet (when the extension proc is called on an
  178. // install from an older profile), the connectoid is cleared so that the
  179. // CustomDialDll part of the connectoid entry is blanked out. This
  180. // prevents RasDeleteEntry being called on the connectoid by older versions
  181. // of cmstp.exe that don't know to clear the entry before calling it.
  182. // Otherwise, the RasCustomDeleteEntryNotify function is called and the whole
  183. // profile is deleted. This will only happen on 1.0 profiles that have
  184. // been dialed with but not migrated/upgraded. Please see NTRAID 379667
  185. // for further details.
  186. //
  187. // Arguments: BOOL bIeak5Profile -- If the calling profile is an IEAK5 CM profile or not
  188. //
  189. // Returns: TRUE if successful
  190. //
  191. // History: quintinb Created 8/2/99
  192. //
  193. //+----------------------------------------------------------------------------
  194. BOOL EnumerateAndPreMigrateAllUserProfiles(BOOL bIeak5Profile)
  195. {
  196. DWORD dwValueSize;
  197. HKEY hKey;
  198. DWORD dwType;
  199. DWORD dwDataSize;
  200. TCHAR szCurrentValue[MAX_PATH+1];
  201. TCHAR szCurrentData[MAX_PATH+1];
  202. //
  203. // Load RasApi32.dll and get RasSetEntryProperties from it.
  204. //
  205. pfnRasSetEntryPropertiesSpec pfnSetEntryProperties = NULL;
  206. HMODULE hRasApi32 = LoadLibrary(TEXT("RASAPI32.DLL"));
  207. if (hRasApi32)
  208. {
  209. pfnSetEntryProperties = (pfnRasSetEntryPropertiesSpec)GetProcAddress(hRasApi32,
  210. "RasSetEntryPropertiesA");
  211. if (NULL == pfnSetEntryProperties)
  212. {
  213. CMTRACE(TEXT("EnumerateAndPreMigrateAllUserProfiles -- Couldn't get RasSetEntryProperties."));
  214. return FALSE;
  215. }
  216. }
  217. else
  218. {
  219. CMTRACE(TEXT("EnumerateAndPreMigrateAllUserProfiles -- Couldn't load rasapi32.dll."));
  220. return FALSE;
  221. }
  222. //
  223. // Get the all user CM and all user PBK directories
  224. //
  225. TCHAR szCmAllUsersDir[MAX_PATH+1] = {0};
  226. LPTSTR pszPhonebook = NULL;
  227. if (GetAllUsersCmDir(szCmAllUsersDir, g_hInst))
  228. {
  229. //
  230. // TRUE is for an All-User profile
  231. //
  232. if (!GetPhoneBookPath(szCmAllUsersDir, &pszPhonebook, TRUE))
  233. {
  234. CMTRACE(TEXT("EnumerateAndPreMigrateAllUserProfiles -- GetPhoneBookPath Failed, returning."));
  235. return FALSE;
  236. }
  237. }
  238. else
  239. {
  240. CMTRACE(TEXT("EnumerateAndPreMigrateAllUserProfiles -- GetAllUsersCmDir Failed, returning."));
  241. return FALSE;
  242. }
  243. //
  244. // If its and IEAK5 profile then we need to get the System Directory
  245. //
  246. TCHAR szSysDir[MAX_PATH+1];
  247. if (bIeak5Profile)
  248. {
  249. if (0 == GetSystemDirectory(szSysDir, MAX_PATH))
  250. {
  251. CMTRACE(TEXT("EnumerateAndPreMigrateAllUserProfiles -- GetSystemDirectory Failed, returning."));
  252. return FALSE;
  253. }
  254. }
  255. //
  256. // Now enumerate all of the All User Profiles and see if they need any
  257. // Pre-Migration.
  258. //
  259. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmMappings, 0, KEY_READ, &hKey))
  260. {
  261. DWORD dwIndex = 0;
  262. dwValueSize = MAX_PATH;
  263. dwDataSize = MAX_PATH;
  264. while (ERROR_SUCCESS == RegEnumValue(hKey, dwIndex, szCurrentValue, &dwValueSize, NULL, &dwType,
  265. (LPBYTE)szCurrentData, &dwDataSize))
  266. {
  267. if (REG_SZ == dwType)
  268. {
  269. MYDBGASSERT(0 != szCurrentValue[0]);
  270. MYDBGASSERT(0 != szCurrentData[0]);
  271. if (ProfileNeedsMigration(szCurrentValue, szCurrentData))
  272. {
  273. //
  274. // Use GetPhoneBookPath to get the path to the phonebook.
  275. //
  276. TCHAR szCmAllUsersDir[MAX_PATH+1] = {0};
  277. //
  278. // Use RasSetEntryProperties to clear the connectoid
  279. //
  280. RASENTRY_V500 RasEntryV5 = {0};
  281. RasEntryV5.dwSize = sizeof(RASENTRY_V500);
  282. RasEntryV5.dwType = RASET_Internet;
  283. if (bIeak5Profile)
  284. {
  285. //
  286. // Since IEAK5 didn't migrate 1.0 connectoids
  287. // properly (it writes them in %windir%\system32\pbk\rasphone.pbk
  288. // instead of under the all users profile as appropriate),
  289. // we need to set the szCustomDialDll instead of clearing it.
  290. //
  291. wsprintf(RasEntryV5.szCustomDialDll, TEXT("%s\\cmdial32.dll"), szSysDir);
  292. }
  293. // else zero the szCustomDialDll part of the entry
  294. // RasEntryV5.szCustomDialDll[0] = TEXT('\0'); -- already zero-ed
  295. DWORD dwRet = ((pfnSetEntryProperties)(pszPhonebook, szCurrentValue,
  296. (RASENTRY*)&RasEntryV5,
  297. RasEntryV5.dwSize, NULL, 0));
  298. if (ERROR_SUCCESS != dwRet)
  299. {
  300. CMTRACE3(TEXT("EnumerateAndPreMigrateAllUserProfiles -- RasSetEntryProperties failed on entry %s in %s, dwRet = %u"), szCurrentValue, MYDBGSTR(pszPhonebook), dwRet);
  301. }
  302. }
  303. }
  304. dwValueSize = MAX_PATH;
  305. dwDataSize = MAX_PATH;
  306. dwIndex++;
  307. }
  308. MYVERIFY(ERROR_SUCCESS == RegCloseKey(hKey));
  309. }
  310. else
  311. {
  312. CMTRACE(TEXT("EnumerateAndPreMigrateAllUserProfiles -- No CM mappings key to migrate."));
  313. }
  314. CmFree(pszPhonebook);
  315. return TRUE;
  316. }
  317. //+----------------------------------------------------------------------------
  318. //
  319. // Function: RunningUnderWow64
  320. //
  321. // Synopsis: This function is used to tell if a 32-bit process is running under
  322. // Wow64 on an ia64 machine. Note that if we are compiled 64-bit this
  323. // is always false. We make the determination by trying to call
  324. // GetSystemWow64Directory. If this function doesn't exist or returns
  325. // ERROR_CALL_NOT_IMPLEMENTED we know we are running on 32-bit. If the
  326. // function returns successfully we know we are running under wow64.
  327. //
  328. // Arguments: None
  329. //
  330. // Returns: BOOL - whether we are executing under wow64 or not
  331. //
  332. // History: quintinb Created 08/18/00
  333. //
  334. //+----------------------------------------------------------------------------
  335. BOOL RunningUnderWow64 ()
  336. {
  337. #ifdef _WIN64
  338. return FALSE;
  339. #else
  340. BOOL bReturn = FALSE;
  341. //
  342. // First get a module handle for kernel32.dll. Note it isn't necessary
  343. // to free this handle as GetModuleHandle doesn't change the ref count.
  344. //
  345. HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32.dll"));
  346. if (hKernel32)
  347. {
  348. //
  349. // Next get the function pointer for GetSystemWow64Directory
  350. //
  351. typedef UINT (WINAPI *pfnGetSystemWow64DirectorySpec)(LPTSTR, UINT);
  352. #ifdef UNICODE
  353. const CHAR* const c_pszGetSystemWow64FuncName = "GetSystemWow64DirectoryW";
  354. #else
  355. const CHAR* const c_pszGetSystemWow64FuncName = "GetSystemWow64DirectoryA";
  356. #endif
  357. pfnGetSystemWow64DirectorySpec pfnGetSystemWow64Directory = (pfnGetSystemWow64DirectorySpec)GetProcAddress(hKernel32, c_pszGetSystemWow64FuncName);
  358. if (pfnGetSystemWow64Directory)
  359. {
  360. TCHAR szSysWow64Path[MAX_PATH+1] = TEXT("");
  361. //
  362. // GetSystemWow64Directory returns the number of chars copied to the buffer.
  363. // If we get zero back, then we need to check the last error code to see what the
  364. // reason for failure was. If it was call not implemented then we know we are
  365. // running on native x86.
  366. //
  367. UINT uReturn = pfnGetSystemWow64Directory(szSysWow64Path, MAX_PATH);
  368. DWORD dwError = GetLastError();
  369. CMTRACE2(TEXT("RunningUnderWow64 -- GetSystemWow64Directory returned \"%s\" and %d"), szSysWow64Path, uReturn);
  370. if (uReturn)
  371. {
  372. bReturn = TRUE;
  373. }
  374. else
  375. {
  376. CMTRACE1(TEXT("RunningUnderWow64 -- GetSystemWow64Directory returned zero, checking GLE=%d"), dwError);
  377. if (ERROR_CALL_NOT_IMPLEMENTED == dwError)
  378. {
  379. bReturn = FALSE;
  380. }
  381. else
  382. {
  383. //
  384. // We got an error, the return value is indeterminant. Let's take a backup method
  385. // of looking for %windir%\syswow64 and see if we can find one.
  386. //
  387. if (GetWindowsDirectory (szSysWow64Path, MAX_PATH))
  388. {
  389. lstrcat(szSysWow64Path, TEXT("\\syswow64"));
  390. HANDLE hDir = CreateFile(szSysWow64Path, GENERIC_READ,
  391. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  392. OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
  393. CMTRACE2(TEXT("RunningUnderWow64 -- Fall back algorithm. Does \"%s\" exist? %d"), szSysWow64Path, (INVALID_HANDLE_VALUE != hDir));
  394. if (INVALID_HANDLE_VALUE != hDir)
  395. {
  396. bReturn = TRUE;
  397. CloseHandle(hDir);
  398. }
  399. }
  400. }
  401. }
  402. }
  403. }
  404. return bReturn;
  405. #endif
  406. }
  407. //+----------------------------------------------------------------------------
  408. //
  409. // Function: CmstpExtensionProc
  410. //
  411. // Synopsis: This function is called by cmstp.exe right after it processes the command
  412. // line and again after it completes its action. It is most useful for modifying
  413. // the install behavior of profiles. Since the cmstp.exe that shipped with the profile,
  414. // not the current version of cmstp.exe is used for the install, this proc
  415. // allows us to change the install flags, change the inf path, and tell cmstp
  416. // to continue or silently fail the install. This version of the proc looks for
  417. // old versions of cmstp.exe and then copies the installation files to a temporary
  418. // directory and then launches the system's version of cmstp.exe with the new
  419. // install directory and parameters (we add the /c switch so that cmstp.exe knows to
  420. // cleanup after itself and to wait on the mutex).
  421. //
  422. // Arguments: LPDWORD pdwFlags - command line flags parsed by cmstp.exe
  423. // LPTSTR pszInfFile - Path to the original INF file
  424. // HRESULT hrRet - Return value of the action, only really used for POST
  425. // EXTENSIONDLLPROCTIMES PreOrPost - PRE or POST, tells when we are being called.
  426. //
  427. // Returns: BOOL - Whether Cmstp.exe should continue the existing install or not.
  428. //
  429. // History: quintinb Created 6/2/99
  430. //
  431. //+----------------------------------------------------------------------------
  432. BOOL CmstpExtensionProc(LPDWORD pdwFlags, LPTSTR pszInfFile, HRESULT hrRet, EXTENSIONDLLPROCTIMES PreOrPost)
  433. {
  434. //
  435. // We don't want 32-bit profiles installing on 64-bit. Note that the 32-bit version of cmcfg32.dll will be
  436. // in the syswow64 dir on a 64-bit machine. Thus the code below will kick in when a 32-bit version of this
  437. // function is used on a 64-bit machine. We also don't want 32-bit profiles trying to do migration, uninstalling,
  438. // etc.
  439. //
  440. if (RunningUnderWow64())
  441. {
  442. //
  443. // If this is an install, show an error message about not being able to install 32-bit profiles
  444. // on 64-bit.
  445. //
  446. if (0 == ((*pdwFlags) & 0xFF))
  447. {
  448. //
  449. // Get the long service name from the Inf
  450. //
  451. TCHAR szServiceName[MAX_PATH+1] = TEXT("");
  452. TCHAR szMsg[MAX_PATH+1] = TEXT("");
  453. MYVERIFY(0 != GetPrivateProfileString(c_pszInfSectionStrings, c_pszCmEntryServiceName, TEXT(""), szServiceName, CELEMS(szServiceName), pszInfFile));
  454. MYVERIFY(0 != LoadString(g_hInst, IDS_NO_I386_ON_IA64, szMsg, MAX_PATH));
  455. MYVERIFY(IDOK == MessageBox(NULL, szMsg, szServiceName, MB_OK));
  456. }
  457. //
  458. // Fail whatever operation we were called to do
  459. //
  460. return FALSE;
  461. }
  462. //
  463. // If the first two Hex digits in the flags value are Zero, then we have an install.
  464. // Otherwise we have some other command that we wish to ignore. We also are only
  465. // interested in processing PRE install calls.
  466. //
  467. if ((0 == ((*pdwFlags) & 0xFF)) && (PRE == PreOrPost))
  468. {
  469. CMTRACE(TEXT("CmstpExtensionProc -- Entering the cmstpex processing loop."));
  470. //
  471. // We only wish to re-launch the install with the current cmstp.exe if we are dealing with an
  472. // older install. Thus check the version stamp in the inf file. We will re-launch any
  473. // profiles with the version number less than the current version number of cmdial32.dll.
  474. //
  475. CmVersion CmVer;
  476. DWORD dwProfileVersionNumber = (DWORD)GetPrivateProfileInt(c_pszSectionCmDial32, c_pszVersion, 0,
  477. pszInfFile);
  478. DWORD dwProfileBuildNumber = (DWORD)GetPrivateProfileInt(c_pszSectionCmDial32, c_pszVerBuild, 0,
  479. pszInfFile);
  480. if ((CmVer.GetVersionNumber() > dwProfileVersionNumber) ||
  481. ((CmVer.GetVersionNumber() == dwProfileVersionNumber) &&
  482. (CmVer.GetBuildAndQfeNumber() > dwProfileBuildNumber)))
  483. {
  484. //
  485. // Then we need to delete the CM bits included with the profile because
  486. // otherwise we will get install errors due to the fact that the profile
  487. // will be launched with the cmdial32.dll from system32 (the path is
  488. // explicitly specified in the connectoid for the custom dial dll), but
  489. // the load path used by the system is the directory from which the exe
  490. // module loaded (the temp dir in this case). Thus we will get a mixed
  491. // set of bits (cmdial32.dll from system32 and cmutil.dll, cmpbk32.dll, etc.
  492. // from the cab).
  493. //
  494. TCHAR szTempDir[MAX_PATH+1];
  495. lstrcpy (szTempDir, pszInfFile);
  496. LPTSTR pszSlash = CmStrrchr(szTempDir, TEXT('\\'));
  497. if (pszSlash)
  498. {
  499. //
  500. // Then we found a last slash, Zero Terminate.
  501. //
  502. *pszSlash = TEXT('\0');
  503. //
  504. // Now we have the old temp dir path. Lets delete the old
  505. // CM bits
  506. //
  507. MYVERIFY(0 != RenameOldCmBits (szTempDir));
  508. }
  509. //
  510. // We also need to make sure that there aren't any 1.0 profiles that have a
  511. // 1.2 connectoid but only have a 1.0 registry format (thus they still have
  512. // a 1.0 desktop GUID interface). The problem here is that installation will try
  513. // to run profile migration on these connectoids. Older versions of cmstp.exe
  514. // would delete the existing connectoids and make new ones during profile migration.
  515. // The problem is that on NT5 we now respond to the RasCustomDeleteEntryNotify call, and
  516. // thus will uninstall profiles that have RasDeleteEntry called on their main connectoid.
  517. // To prevent this, we must pre-migrate older profiles and delete the new connectoid
  518. // properly.
  519. //
  520. EnumerateAndPreMigrateAllUserProfiles(IsIeak5Cm(dwProfileVersionNumber, dwProfileBuildNumber));
  521. }
  522. }
  523. return TRUE; // always return TRUE so that cmstp.exe continues. Only change this if you want cmstp.exe
  524. // to fail certain actions.
  525. }