1 #ifdef __NMAKE__ 2 3 # NMAKE portion. 4 # Build with : nmake /f custom.cpp 5 # Clean with : nmake /f custom.cpp clean 6 7 # Builds custom.dll 8 9 OUTPATH = . 10 11 # program name macros 12 CC = cl /nologo 13 14 LINK = link /nologo 15 16 RM = del 17 18 DLLFILE = $(OUTPATH)\custom.dll 19 20 DLLEXPORTS =\ 21 -EXPORT:EnableAllowTgtSessionKey \ 22 -EXPORT:RevertAllowTgtSessionKey \ 23 -EXPORT:AbortMsiImmediate \ 24 -EXPORT:UninstallNsisInstallation \ 25 -EXPORT:KillRunningProcesses \ 26 -EXPORT:ListRunningProcesses \ 27 -EXPORT:InstallNetProvider \ 28 -EXPORT:UninstallNetProvider 29 30 $(DLLFILE): $(OUTPATH)\custom.obj 31 $(LINK) /OUT:$@ /DLL $** $(DLLEXPORTS) 32 33 $(OUTPATH)\custom.obj: custom.cpp custom.h 34 $(CC) /c /Fo$@ custom.cpp 35 36 all: $(DLLFILE) 37 38 clean: 39 $(RM) $(DLLFILE) 40 $(RM) $(OUTPATH)\custom.obj 41 $(RM) $(OUTPATH)\custom.exp 42 43 !IFDEF __C_TEXT__ 44 #else 45 /* 46 47 Copyright 2004,2005 by the Massachusetts Institute of Technology 48 49 All rights reserved. 50 51 Permission to use, copy, modify, and distribute this software and its 52 documentation for any purpose and without fee is hereby granted, 53 provided that the above copyright notice appear in all copies and that 54 both that copyright notice and this permission notice appear in 55 supporting documentation, and that the name of the Massachusetts 56 Institute of Technology (M.I.T.) not be used in advertising or publicity 57 pertaining to distribution of the software without specific, written 58 prior permission. 59 60 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 61 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 62 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 63 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 64 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 65 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 66 SOFTWARE. 67 68 */ 69 70 /************************************************************** 71 * custom.cpp : Dll implementing custom action to install Kerberos for Windows 72 * 73 * The functions in this file are for use as entry points 74 * for calls from MSI only. The specific MSI parameters 75 * are noted in the comments section of each of the 76 * functions. 77 * 78 * rcsid: $Id$ 79 **************************************************************/ 80 81 #pragma unmanaged 82 83 // Only works for Win2k and above 84 #define _WIN32_WINNT 0x500 85 #include "custom.h" 86 #include <shellapi.h> 87 88 // linker stuff 89 #pragma comment(lib, "msi") 90 #pragma comment(lib, "advapi32") 91 #pragma comment(lib, "shell32") 92 #pragma comment(lib, "user32") 93 94 void ShowMsiError( MSIHANDLE hInstall, DWORD errcode, DWORD param ){ 95 MSIHANDLE hRecord; 96 97 hRecord = MsiCreateRecord(3); 98 MsiRecordClearData(hRecord); 99 MsiRecordSetInteger(hRecord, 1, errcode); 100 MsiRecordSetInteger(hRecord, 2, param); 101 102 MsiProcessMessage( hInstall, INSTALLMESSAGE_ERROR, hRecord ); 103 104 MsiCloseHandle( hRecord ); 105 } 106 107 static void ShowMsiErrorEx(MSIHANDLE hInstall, DWORD errcode, LPTSTR str, 108 DWORD param ) 109 { 110 MSIHANDLE hRecord; 111 112 hRecord = MsiCreateRecord(3); 113 MsiRecordClearData(hRecord); 114 MsiRecordSetInteger(hRecord, 1, errcode); 115 MsiRecordSetString(hRecord, 2, str); 116 MsiRecordSetInteger(hRecord, 3, param); 117 118 MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecord); 119 120 MsiCloseHandle(hRecord); 121 } 122 123 #define LSA_KERBEROS_KEY "SYSTEM\\CurrentControlSet\\Control\\Lsa\\Kerberos" 124 #define LSA_KERBEROS_PARM_KEY "SYSTEM\\CurrentControlSet\\Control\\Lsa\\Kerberos\\Parameters" 125 #define KFW_CLIENT_KEY "SOFTWARE\\MIT\\Kerberos\\Client\\" 126 #define SESSKEY_VALUE_NAME "AllowTGTSessionKey" 127 128 #define SESSBACKUP_VALUE_NAME "AllowTGTSessionKeyBackup" 129 #define SESSXPBACKUP_VALUE_NAME "AllowTGTSessionKeyBackupXP" 130 131 132 /* Set the AllowTGTSessionKey registry keys on install. Called as a deferred custom action. */ 133 MSIDLLEXPORT EnableAllowTgtSessionKey( MSIHANDLE hInstall ) { 134 return SetAllowTgtSessionKey( hInstall, TRUE ); 135 } 136 137 /* Unset the AllowTGTSessionKey registry keys on uninstall. Called as a deferred custom action. */ 138 MSIDLLEXPORT RevertAllowTgtSessionKey( MSIHANDLE hInstall ) { 139 return SetAllowTgtSessionKey( hInstall, FALSE ); 140 } 141 142 UINT SetAllowTgtSessionKey( MSIHANDLE hInstall, BOOL pInstall ) { 143 TCHAR tchVersionString[1024]; 144 TCHAR tchVersionKey[2048]; 145 DWORD size; 146 DWORD type; 147 DWORD value; 148 HKEY hkKfwClient = NULL; 149 HKEY hkLsaKerberos = NULL; 150 HKEY hkLsaKerberosParm = NULL; 151 UINT rv; 152 DWORD phase = 0; 153 154 // construct the backup key path 155 size = sizeof(tchVersionString) / sizeof(TCHAR); 156 rv = MsiGetProperty( hInstall, _T("CustomActionData"), tchVersionString, &size ); 157 if(rv != ERROR_SUCCESS) { 158 if(pInstall) { 159 ShowMsiError( hInstall, ERR_CUSTACTDATA, rv ); 160 return rv; 161 } else { 162 return ERROR_SUCCESS; 163 } 164 } 165 166 _tcscpy( tchVersionKey, _T( KFW_CLIENT_KEY ) ); 167 _tcscat( tchVersionKey, tchVersionString ); 168 169 phase = 1; 170 171 rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, tchVersionKey, 0, ((pInstall)?KEY_WRITE:KEY_READ), &hkKfwClient ); 172 if(rv != ERROR_SUCCESS) 173 goto cleanup; 174 175 phase = 2; 176 177 rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, _T( LSA_KERBEROS_KEY ), 0, KEY_READ | KEY_WRITE, &hkLsaKerberos ); 178 if(rv != ERROR_SUCCESS) 179 goto cleanup; 180 181 phase = 3; 182 183 rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, _T( LSA_KERBEROS_PARM_KEY ), 0, KEY_READ | KEY_WRITE, &hkLsaKerberosParm ); 184 if(rv != ERROR_SUCCESS) { 185 hkLsaKerberosParm = NULL; 186 } 187 188 if(pInstall) { 189 // backup the existing values 190 if(hkLsaKerberosParm) { 191 phase = 4; 192 193 size = sizeof(value); 194 rv = RegQueryValueEx( hkLsaKerberosParm, _T( SESSKEY_VALUE_NAME ), NULL, &type, (LPBYTE) &value, &size ); 195 if(rv != ERROR_SUCCESS) 196 value = 0; 197 198 phase = 5; 199 rv = RegSetValueEx( hkKfwClient, _T( SESSBACKUP_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value)); 200 if(rv != ERROR_SUCCESS) 201 goto cleanup; 202 } 203 204 phase = 6; 205 size = sizeof(value); 206 rv = RegQueryValueEx( hkLsaKerberos, _T( SESSKEY_VALUE_NAME ), NULL, &type, (LPBYTE) &value, &size ); 207 if(rv != ERROR_SUCCESS) 208 value = 0; 209 210 phase = 7; 211 rv = RegSetValueEx( hkKfwClient, _T( SESSXPBACKUP_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value)); 212 if(rv != ERROR_SUCCESS) 213 goto cleanup; 214 215 // and now write the actual values 216 phase = 8; 217 value = 1; 218 rv = RegSetValueEx( hkLsaKerberos, _T( SESSKEY_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value)); 219 if(rv != ERROR_SUCCESS) 220 goto cleanup; 221 222 if(hkLsaKerberosParm) { 223 phase = 9; 224 value = 1; 225 rv = RegSetValueEx( hkLsaKerberosParm, _T( SESSKEY_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value)); 226 if(rv != ERROR_SUCCESS) 227 goto cleanup; 228 } 229 230 } else { // uninstalling 231 // Don't fail no matter what goes wrong. This is also a rollback action. 232 if(hkLsaKerberosParm) { 233 size = sizeof(value); 234 rv = RegQueryValueEx( hkKfwClient, _T( SESSBACKUP_VALUE_NAME ), NULL, &type, (LPBYTE) &value, &size ); 235 if(rv != ERROR_SUCCESS) 236 value = 0; 237 238 RegSetValueEx( hkLsaKerberosParm, _T( SESSKEY_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value)); 239 } 240 241 size = sizeof(value); 242 rv = RegQueryValueEx( hkKfwClient, _T( SESSXPBACKUP_VALUE_NAME ), NULL, &type, (LPBYTE) &value, &size ); 243 if(rv != ERROR_SUCCESS) 244 value = 0; 245 246 RegSetValueEx( hkLsaKerberos, _T( SESSKEY_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value)); 247 248 RegDeleteValue( hkKfwClient, _T( SESSXPBACKUP_VALUE_NAME ) ); 249 RegDeleteValue( hkKfwClient, _T( SESSBACKUP_VALUE_NAME ) ); 250 } 251 252 // all done 253 rv = ERROR_SUCCESS; 254 255 cleanup: 256 if(rv != ERROR_SUCCESS && pInstall) { 257 ShowMsiError(hInstall, 4005, phase); 258 } 259 if(hkKfwClient) RegCloseKey( hkKfwClient ); 260 if(hkLsaKerberos) RegCloseKey( hkLsaKerberos ); 261 if(hkLsaKerberosParm) RegCloseKey( hkLsaKerberosParm ); 262 263 return rv; 264 } 265 266 /* Abort the installation (called as an immediate custom action) */ 267 MSIDLLEXPORT AbortMsiImmediate( MSIHANDLE hInstall ) { 268 DWORD rv; 269 DWORD dwSize = 0; 270 LPTSTR sReason = NULL; 271 LPTSTR sFormatted = NULL; 272 MSIHANDLE hRecord = NULL; 273 LPTSTR cAbortReason = _T("ABORTREASON"); 274 275 rv = MsiGetProperty( hInstall, cAbortReason, _T(""), &dwSize ); 276 if(rv != ERROR_MORE_DATA) goto _cleanup; 277 278 sReason = new TCHAR[ ++dwSize ]; 279 280 rv = MsiGetProperty( hInstall, cAbortReason, sReason, &dwSize ); 281 282 if(rv != ERROR_SUCCESS) goto _cleanup; 283 284 hRecord = MsiCreateRecord(3); 285 MsiRecordClearData(hRecord); 286 MsiRecordSetString(hRecord, 0, sReason); 287 288 dwSize = 0; 289 290 rv = MsiFormatRecord(hInstall, hRecord, "", &dwSize); 291 if(rv != ERROR_MORE_DATA) goto _cleanup; 292 293 sFormatted = new TCHAR[ ++dwSize ]; 294 295 rv = MsiFormatRecord(hInstall, hRecord, sFormatted, &dwSize); 296 297 if(rv != ERROR_SUCCESS) goto _cleanup; 298 299 MsiCloseHandle(hRecord); 300 301 hRecord = MsiCreateRecord(3); 302 MsiRecordClearData(hRecord); 303 MsiRecordSetInteger(hRecord, 1, ERR_ABORT); 304 MsiRecordSetString(hRecord,2, sFormatted); 305 MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecord); 306 307 _cleanup: 308 if(sFormatted) delete sFormatted; 309 if(hRecord) MsiCloseHandle( hRecord ); 310 if(sReason) delete sReason; 311 312 return ~ERROR_SUCCESS; 313 } 314 315 /* Kill specified processes that are running on the system */ 316 /* Uses the custom table KillProcess. Called as an immediate action. */ 317 318 #define MAX_KILL_PROCESSES 255 319 #define FIELD_SIZE 256 320 321 struct _KillProc { 322 TCHAR * image; 323 TCHAR * desc; 324 BOOL found; 325 DWORD pid; 326 }; 327 328 #define RV_BAIL if(rv != ERROR_SUCCESS) goto _cleanup 329 330 MSIDLLEXPORT KillRunningProcesses( MSIHANDLE hInstall ) { 331 return KillRunningProcessesWorker( hInstall, TRUE ); 332 } 333 334 /* When listing running processes, we populate the ListBox table with 335 values associated with the property 'KillableProcesses'. If we 336 actually find any processes worth killing, then we also set the 337 'FoundProcceses' property to '1'. Otherwise we set it to ''. 338 */ 339 340 MSIDLLEXPORT ListRunningProcesses( MSIHANDLE hInstall ) { 341 return KillRunningProcessesWorker( hInstall, FALSE ); 342 } 343 344 UINT KillRunningProcessesWorker( MSIHANDLE hInstall, BOOL bKill ) 345 { 346 UINT rv = ERROR_SUCCESS; 347 _KillProc * kpList; 348 int nKpList = 0; 349 int i; 350 int rowNum = 1; 351 DWORD size; 352 BOOL found = FALSE; 353 354 MSIHANDLE hDatabase = NULL; 355 MSIHANDLE hView = NULL; 356 MSIHANDLE hViewInsert = NULL; 357 MSIHANDLE hRecord = NULL; 358 MSIHANDLE hRecordInsert = NULL; 359 360 HANDLE hSnapshot = NULL; 361 362 PROCESSENTRY32 pe; 363 364 kpList = new _KillProc[MAX_KILL_PROCESSES]; 365 memset(kpList, 0, sizeof(*kpList) * MAX_KILL_PROCESSES); 366 367 hDatabase = MsiGetActiveDatabase( hInstall ); 368 if( hDatabase == NULL ) { 369 rv = GetLastError(); 370 goto _cleanup; 371 } 372 373 // If we are only going to list out the processes, delete all the existing 374 // entries first. 375 376 if(!bKill) { 377 378 rv = MsiDatabaseOpenView( hDatabase, 379 _T( "DELETE FROM `ListBox` WHERE `ListBox`.`Property` = 'KillableProcesses'" ), 380 &hView); RV_BAIL; 381 382 rv = MsiViewExecute( hView, NULL ); RV_BAIL; 383 384 MsiCloseHandle( hView ); 385 386 hView = NULL; 387 388 rv = MsiDatabaseOpenView( hDatabase, 389 _T( "SELECT * FROM `ListBox` WHERE `Property` = 'KillableProcesses'" ), 390 &hViewInsert); RV_BAIL; 391 392 MsiViewExecute(hViewInsert, NULL); 393 394 hRecordInsert = MsiCreateRecord(4); 395 396 if(hRecordInsert == NULL) { 397 rv = GetLastError(); 398 goto _cleanup; 399 } 400 } 401 402 // Open a view 403 rv = MsiDatabaseOpenView( hDatabase, 404 _T( "SELECT `Image`,`Desc` FROM `KillProcess`" ), 405 &hView); RV_BAIL; 406 407 rv = MsiViewExecute( hView, NULL ); RV_BAIL; 408 409 do { 410 rv = MsiViewFetch( hView, &hRecord ); 411 if(rv != ERROR_SUCCESS) { 412 if(hRecord) 413 MsiCloseHandle(hRecord); 414 hRecord = NULL; 415 break; 416 } 417 418 kpList[nKpList].image = new TCHAR[ FIELD_SIZE ]; kpList[nKpList].image[0] = _T('\0'); 419 kpList[nKpList].desc = new TCHAR[ FIELD_SIZE ]; kpList[nKpList].desc[0] = _T('\0'); 420 nKpList++; 421 422 size = FIELD_SIZE; 423 rv = MsiRecordGetString(hRecord, 1, kpList[nKpList-1].image, &size); RV_BAIL; 424 425 size = FIELD_SIZE; 426 rv = MsiRecordGetString(hRecord, 2, kpList[nKpList-1].desc, &size); RV_BAIL; 427 428 MsiCloseHandle(hRecord); 429 } while(nKpList < MAX_KILL_PROCESSES); 430 431 hRecord = NULL; 432 433 // now we have all the processes in the array. Check if they are running. 434 435 hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); 436 if(hSnapshot == INVALID_HANDLE_VALUE) { 437 rv = GetLastError(); 438 goto _cleanup; 439 } 440 441 pe.dwSize = sizeof( PROCESSENTRY32 ); 442 443 if(!Process32First( hSnapshot, &pe )) { 444 // technically we should at least find the MSI process, but we let this pass 445 rv = ERROR_SUCCESS; 446 goto _cleanup; 447 } 448 449 do { 450 for(i=0; i<nKpList; i++) { 451 if(!_tcsicmp( kpList[i].image, pe.szExeFile )) { 452 // got one 453 if(bKill) { 454 // try to kill the process 455 HANDLE hProcess = NULL; 456 457 // If we encounter an error, instead of bailing 458 // out, we continue on to the next process. We 459 // may not have permission to kill all the 460 // processes we want to kill anyway. If there are 461 // any files that we want to replace that is in 462 // use, Windows Installer will schedule a reboot. 463 // Also, it's not like we have an exhaustive list 464 // of all the programs that use Kerberos anyway. 465 466 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe.th32ProcessID); 467 if(hProcess == NULL) { 468 rv = GetLastError(); 469 break; 470 } 471 472 if(!TerminateProcess(hProcess, 0)) { 473 rv = GetLastError(); 474 CloseHandle(hProcess); 475 break; 476 } 477 478 CloseHandle(hProcess); 479 480 } else { 481 TCHAR buf[256]; 482 483 // we are supposed to just list out the processes 484 rv = MsiRecordClearData( hRecordInsert ); RV_BAIL; 485 rv = MsiRecordSetString( hRecordInsert, 1, _T("KillableProcesses")); 486 rv = MsiRecordSetInteger( hRecordInsert, 2, rowNum++ ); RV_BAIL; 487 _itot( rowNum, buf, 10 ); 488 rv = MsiRecordSetString( hRecordInsert, 3, buf ); RV_BAIL; 489 if(_tcslen(kpList[i].desc)) { 490 rv = MsiRecordSetString( hRecordInsert, 4, kpList[i].desc ); RV_BAIL; 491 } else { 492 rv = MsiRecordSetString( hRecordInsert, 4, kpList[i].image ); RV_BAIL; 493 } 494 MsiViewModify(hViewInsert, MSIMODIFY_INSERT_TEMPORARY, hRecordInsert); RV_BAIL; 495 496 found = TRUE; 497 } 498 break; 499 } 500 } 501 } while( Process32Next( hSnapshot, &pe ) ); 502 503 if(!bKill) { 504 // set the 'FoundProcceses' property 505 if(found) { 506 MsiSetProperty( hInstall, _T("FoundProcesses"), _T("1")); 507 } else { 508 MsiSetProperty( hInstall, _T("FoundProcesses"), _T("")); 509 } 510 } 511 512 // Finally: 513 rv = ERROR_SUCCESS; 514 515 _cleanup: 516 517 if(hRecordInsert) MsiCloseHandle(hRecordInsert); 518 if(hViewInsert) MsiCloseHandle(hView); 519 520 if(hSnapshot && hSnapshot != INVALID_HANDLE_VALUE) CloseHandle(hSnapshot); 521 522 while(nKpList) { 523 nKpList--; 524 delete kpList[nKpList].image; 525 delete kpList[nKpList].desc; 526 } 527 delete kpList; 528 529 if(hRecord) MsiCloseHandle(hRecord); 530 if(hView) MsiCloseHandle(hView); 531 532 if(hDatabase) MsiCloseHandle(hDatabase); 533 534 if(rv != ERROR_SUCCESS) { 535 ShowMsiError(hInstall, ERR_PROC_LIST, rv); 536 } 537 538 return rv; 539 } 540 541 static bool IsNSISInstalled() 542 { 543 HKEY nsisKfwKey = NULL; 544 // Note: check Wow6432 node if 64 bit build 545 HRESULT res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 546 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion" 547 "\\Uninstall\\Kerberos for Windows", 548 0, 549 KEY_READ | KEY_WOW64_32KEY, 550 &nsisKfwKey); 551 if (res != ERROR_SUCCESS) 552 return FALSE; 553 554 RegCloseKey(nsisKfwKey); 555 return TRUE; 556 } 557 558 static HANDLE NSISUninstallShellExecute(LPTSTR pathUninstall) 559 { 560 SHELLEXECUTEINFO sei; 561 ZeroMemory ( &sei, sizeof(sei) ); 562 563 sei.cbSize = sizeof(sei); 564 sei.hwnd = GetForegroundWindow(); 565 sei.fMask = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI | 566 SEE_MASK_NOCLOSEPROCESS; 567 sei.lpVerb = _T("runas"); // run as administrator 568 sei.lpFile = pathUninstall; 569 sei.lpParameters = _T(""); 570 sei.nShow = SW_SHOWNORMAL; 571 572 if (!ShellExecuteEx(&sei)) { 573 // FAILED! TODO: report details? 574 } 575 return sei.hProcess; 576 } 577 578 static HANDLE NSISUninstallCreateProcess(LPTSTR pathUninstall) 579 { 580 STARTUPINFO sInfo; 581 PROCESS_INFORMATION pInfo; 582 pInfo.hProcess = NULL; 583 pInfo.hThread = NULL; 584 585 // Create a process for the uninstaller 586 sInfo.cb = sizeof(sInfo); 587 sInfo.lpReserved = NULL; 588 sInfo.lpDesktop = _T(""); 589 sInfo.lpTitle = _T("NSIS Uninstaller for Kerberos for Windows"); 590 sInfo.dwX = 0; 591 sInfo.dwY = 0; 592 sInfo.dwXSize = 0; 593 sInfo.dwYSize = 0; 594 sInfo.dwXCountChars = 0; 595 sInfo.dwYCountChars = 0; 596 sInfo.dwFillAttribute = 0; 597 sInfo.dwFlags = 0; 598 sInfo.wShowWindow = 0; 599 sInfo.cbReserved2 = 0; 600 sInfo.lpReserved2 = 0; 601 sInfo.hStdInput = 0; 602 sInfo.hStdOutput = 0; 603 sInfo.hStdError = 0; 604 605 if (!CreateProcess(pathUninstall, 606 _T("Uninstall /S"), 607 NULL, 608 NULL, 609 FALSE, 610 CREATE_SUSPENDED, 611 NULL, 612 NULL, 613 &sInfo, 614 &pInfo)) { 615 // failure; could grab info, but we should be able to recover by 616 // using NSISUninstallShellExecute... 617 } else { 618 // success 619 // start up the thread 620 ResumeThread(pInfo.hThread); 621 // done with thread handle 622 CloseHandle(pInfo.hThread); 623 } 624 return pInfo.hProcess; 625 } 626 627 628 /* Uninstall NSIS */ 629 MSIDLLEXPORT UninstallNsisInstallation( MSIHANDLE hInstall ) 630 { 631 DWORD rv = ERROR_SUCCESS; 632 DWORD lastError; 633 // lookup the NSISUNINSTALL property value 634 LPTSTR cNsisUninstall = _T("UPGRADENSIS"); 635 LPTSTR strPathUninst = NULL; 636 DWORD dwSize = 0; 637 HANDLE hProcess = NULL; 638 HANDLE hIo = NULL; 639 HANDLE hJob = NULL; 640 641 rv = MsiGetProperty( hInstall, cNsisUninstall, _T(""), &dwSize ); 642 if(rv != ERROR_MORE_DATA) goto _cleanup; 643 644 strPathUninst = new TCHAR[ ++dwSize ]; 645 646 rv = MsiGetProperty(hInstall, cNsisUninstall, strPathUninst, &dwSize); 647 if(rv != ERROR_SUCCESS) goto _cleanup; 648 649 hProcess = NSISUninstallCreateProcess(strPathUninst); 650 if (hProcess == NULL) // expected when run on UAC-limited account 651 hProcess = NSISUninstallShellExecute(strPathUninst); 652 653 if (hProcess == NULL) { 654 // still no uninstall process? ick... 655 lastError = GetLastError(); 656 rv = 40; 657 goto _cleanup; 658 } 659 // note that it is not suffiecient to wait for the initial process to 660 // finish; there is a whole process tree that we need to wait for. sigh. 661 JOBOBJECT_ASSOCIATE_COMPLETION_PORT acp; 662 acp.CompletionKey = 0; 663 hJob = CreateJobObject(NULL, _T("NSISUninstallObject")); 664 if(!hJob) { 665 rv = 41; 666 goto _cleanup; 667 } 668 669 hIo = CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0); 670 if(!hIo) { 671 rv = 42; 672 goto _cleanup; 673 } 674 675 acp.CompletionPort = hIo; 676 677 SetInformationJobObject(hJob, 678 JobObjectAssociateCompletionPortInformation, 679 &acp, 680 sizeof(acp)); 681 682 AssignProcessToJobObject(hJob, hProcess); 683 684 DWORD msgId; 685 ULONG_PTR unusedCompletionKey; 686 LPOVERLAPPED unusedOverlapped; 687 for (;;) { 688 if (!GetQueuedCompletionStatus(hIo, 689 &msgId, 690 &unusedCompletionKey, 691 &unusedOverlapped, 692 INFINITE)) { 693 Sleep(1000); 694 } else if (msgId == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) { 695 break; 696 } 697 } 698 699 _cleanup: 700 if (hProcess) CloseHandle(hProcess); 701 if (hIo) CloseHandle(hIo); 702 if (hJob) CloseHandle(hJob); 703 704 if (IsNSISInstalled()) { 705 // uninstall failed: maybe user cancelled uninstall, or something else 706 // went wrong... 707 if (rv == ERROR_SUCCESS) 708 rv = 43; 709 } else { 710 // Maybe something went wrong, but it doesn't matter as long as nsis 711 // is gone now... 712 rv = ERROR_SUCCESS; 713 } 714 715 if (rv == 40) { 716 // CreateProcess() / ShellExecute() errors get extra data 717 ShowMsiErrorEx(hInstall, ERR_NSS_FAILED_CP, strPathUninst, lastError); 718 } else if (rv != ERROR_SUCCESS) { 719 ShowMsiError(hInstall, ERR_NSS_FAILED, rv); 720 } 721 722 if (strPathUninst) delete strPathUninst; 723 return rv; 724 } 725 726 /* Check and add or remove networkprovider key value 727 str : target string 728 str2: string to add/remove 729 bInst: == 1 if string should be added to target if not already there, 730 otherwise remove string from target if present. 731 */ 732 int npi_CheckAndAddRemove( LPTSTR str, LPTSTR str2, int bInst ) { 733 734 LPTSTR target, charset, match; 735 int ret=0; 736 737 target = new TCHAR[lstrlen(str)+3]; 738 lstrcpy(target,_T(",")); 739 lstrcat(target,str); 740 lstrcat(target,_T(",")); 741 charset = new TCHAR[lstrlen(str2)+3]; 742 lstrcpy(charset,_T(",")); 743 lstrcat(charset,str2); 744 lstrcat(charset,_T(",")); 745 746 match = _tcsstr(target, charset); 747 748 if ((match) && (bInst)) { 749 ret = INP_ERR_PRESENT; 750 goto cleanup; 751 } 752 753 if ((!match) && (!bInst)) { 754 ret = INP_ERR_ABSENT; 755 goto cleanup; 756 } 757 758 if (bInst) // && !match 759 { 760 lstrcat(str, _T(",")); 761 lstrcat(str, str2); 762 ret = INP_ERR_ADDED; 763 goto cleanup; 764 } 765 766 // if (!bInst) && (match) 767 { 768 lstrcpy(str+(match-target),match+lstrlen(str2)+2); 769 str[lstrlen(str)-1]=_T('\0'); 770 ret = INP_ERR_REMOVED; 771 goto cleanup; 772 } 773 774 cleanup: 775 776 delete target; 777 delete charset; 778 return ret; 779 } 780 781 /* Sets the registry keys required for the functioning of the network provider */ 782 783 DWORD InstNetProvider(MSIHANDLE hInstall, int bInst) { 784 LPTSTR strOrder; 785 HKEY hkOrder; 786 LONG rv; 787 DWORD dwSize; 788 HANDLE hProcHeap; 789 790 strOrder = (LPTSTR) 0; 791 792 CHECK(rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, STR_KEY_ORDER, 0, KEY_READ | KEY_WRITE, &hkOrder )); 793 794 dwSize = 0; 795 CHECK(rv = RegQueryValueEx( hkOrder, STR_VAL_ORDER, NULL, NULL, NULL, &dwSize ) ); 796 797 strOrder = new TCHAR[ (dwSize + STR_SERVICE_LEN + 4) * sizeof(TCHAR) ]; 798 799 CHECK(rv = RegQueryValueEx( hkOrder, STR_VAL_ORDER, NULL, NULL, (LPBYTE) strOrder, &dwSize)); 800 801 strOrder[dwSize] = '\0'; /* reg strings are not always nul terminated */ 802 803 npi_CheckAndAddRemove( strOrder, STR_SERVICE , bInst); 804 805 dwSize = (lstrlen( strOrder ) + 1) * sizeof(TCHAR); 806 807 CHECK(rv = RegSetValueEx( hkOrder, STR_VAL_ORDER, NULL, REG_SZ, (LPBYTE) strOrder, dwSize )); 808 809 /* everything else should be set by the MSI tables */ 810 rv = ERROR_SUCCESS; 811 _cleanup: 812 813 if( rv != ERROR_SUCCESS ) { 814 ShowMsiError( hInstall, ERR_NPI_FAILED, rv ); 815 } 816 817 if(strOrder) delete strOrder; 818 819 return rv; 820 } 821 822 MSIDLLEXPORT InstallNetProvider( MSIHANDLE hInstall ) { 823 return InstNetProvider( hInstall, 1 ); 824 } 825 826 MSIDLLEXPORT UninstallNetProvider( MSIHANDLE hInstall) { 827 return InstNetProvider( hInstall, 0 ); 828 } 829 830 #endif 831 #ifdef __NMAKE__ 832 !ENDIF 833 #endif 834