OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // mini_installer.exe is the first exe that is run when chrome is being | 5 // mini_installer.exe is the first exe that is run when chrome is being |
6 // installed or upgraded. It is designed to be extremely small (~5KB with no | 6 // installed or upgraded. It is designed to be extremely small (~5KB with no |
7 // extra resources linked) and it has two main jobs: | 7 // extra resources linked) and it has two main jobs: |
8 // 1) unpack the resources (possibly decompressing some) | 8 // 1) unpack the resources (possibly decompressing some) |
9 // 2) run the real installer (setup.exe) with appropriate flags. | 9 // 2) run the real installer (setup.exe) with appropriate flags. |
10 // | 10 // |
11 // In order to be really small the app doesn't link against the CRT and | 11 // In order to be really small the app doesn't link against the CRT and |
12 // defines the following compiler/linker flags: | 12 // defines the following compiler/linker flags: |
13 // EnableIntrinsicFunctions="true" compiler: /Oi | 13 // EnableIntrinsicFunctions="true" compiler: /Oi |
14 // BasicRuntimeChecks="0" | 14 // BasicRuntimeChecks="0" |
15 // BufferSecurityCheck="false" compiler: /GS- | 15 // BufferSecurityCheck="false" compiler: /GS- |
16 // EntryPointSymbol="MainEntryPoint" linker: /ENTRY | 16 // EntryPointSymbol="MainEntryPoint" linker: /ENTRY |
17 // IgnoreAllDefaultLibraries="true" linker: /NODEFAULTLIB | 17 // IgnoreAllDefaultLibraries="true" linker: /NODEFAULTLIB |
18 // OptimizeForWindows98="1" liker: /OPT:NOWIN98 | 18 // OptimizeForWindows98="1" liker: /OPT:NOWIN98 |
19 // linker: /SAFESEH:NO | 19 // linker: /SAFESEH:NO |
20 | 20 |
21 // have the linker merge the sections, saving us ~500 bytes. | 21 // have the linker merge the sections, saving us ~500 bytes. |
22 #pragma comment(linker, "/MERGE:.rdata=.text") | 22 #pragma comment(linker, "/MERGE:.rdata=.text") |
23 | 23 |
24 #include <windows.h> | 24 #include <windows.h> |
25 | |
26 // #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the | |
27 // "Community Additions" comment on MSDN here: | |
28 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx | |
29 #define SystemFunction036 NTAPI SystemFunction036 | |
30 #include <NTSecAPI.h> | |
31 #undef SystemFunction036 | |
32 | |
33 #include <sddl.h> | |
25 #include <shellapi.h> | 34 #include <shellapi.h> |
26 #include <stdlib.h> | 35 #include <stdlib.h> |
27 | 36 |
28 #include "chrome/installer/mini_installer/appid.h" | 37 #include "chrome/installer/mini_installer/appid.h" |
29 #include "chrome/installer/mini_installer/configuration.h" | 38 #include "chrome/installer/mini_installer/configuration.h" |
30 #include "chrome/installer/mini_installer/decompress.h" | 39 #include "chrome/installer/mini_installer/decompress.h" |
31 #include "chrome/installer/mini_installer/exit_code.h" | 40 #include "chrome/installer/mini_installer/exit_code.h" |
32 #include "chrome/installer/mini_installer/mini_installer_constants.h" | 41 #include "chrome/installer/mini_installer/mini_installer_constants.h" |
33 #include "chrome/installer/mini_installer/mini_string.h" | 42 #include "chrome/installer/mini_installer/mini_string.h" |
34 #include "chrome/installer/mini_installer/pe_resource.h" | 43 #include "chrome/installer/mini_installer/pe_resource.h" |
(...skipping 497 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
532 // Deletes given files and working dir. | 541 // Deletes given files and working dir. |
533 void DeleteExtractedFiles(const wchar_t* base_path, | 542 void DeleteExtractedFiles(const wchar_t* base_path, |
534 const wchar_t* archive_path, | 543 const wchar_t* archive_path, |
535 const wchar_t* setup_path) { | 544 const wchar_t* setup_path) { |
536 ::DeleteFile(archive_path); | 545 ::DeleteFile(archive_path); |
537 ::DeleteFile(setup_path); | 546 ::DeleteFile(setup_path); |
538 // Delete the temp dir (if it is empty, otherwise fail). | 547 // Delete the temp dir (if it is empty, otherwise fail). |
539 ::RemoveDirectory(base_path); | 548 ::RemoveDirectory(base_path); |
540 } | 549 } |
541 | 550 |
551 // Returns true if the supplied path supports ACLs. | |
552 bool IsAclSupportedForPath(const wchar_t* path) { | |
553 PathString volume; | |
554 DWORD flags = 0; | |
555 return ::GetVolumePathName(path, volume.get(), volume.capacity()) && | |
556 ::GetVolumeInformation(volume.get(), NULL, 0, NULL, NULL, &flags, | |
557 NULL, 0) && (flags & FILE_PERSISTENT_ACLS); | |
558 } | |
559 | |
560 // Retrieves the SID of the default owner for objects created by this user | |
561 // token (accounting for different behavior under UAC elevation, etc.). | |
562 // NOTE: On success the |sid| parameter must be freed with LocalFree(). | |
563 bool GetCurrentOwnerSid(wchar_t** sid) { | |
564 HANDLE token; | |
565 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) | |
566 return false; | |
567 | |
568 DWORD size = 0; | |
569 TOKEN_OWNER* owner = NULL; | |
570 bool result = false; | |
571 // We get the TokenOwner rather than the TokenUser because e.g. under UAC | |
572 // elevation we want the admin to own the directory rather than the user. | |
573 ::GetTokenInformation(token, TokenOwner, &owner, 0, &size); | |
574 if (size && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { | |
575 if (owner = reinterpret_cast<TOKEN_OWNER*>(::LocalAlloc(LPTR, size))) { | |
576 if (::GetTokenInformation(token, TokenOwner, owner, size, &size)) | |
577 result = ::ConvertSidToStringSid(owner->Owner, sid); | |
578 ::LocalFree(owner); | |
579 } | |
580 } | |
581 ::CloseHandle(token); | |
582 return result; | |
583 } | |
584 | |
585 // Populates |sd| suitable for use when creating directories within |path| with | |
586 // ACLs allowing access to only the current owner, admin, and system. | |
587 // NOTE: On success the |sd| parameter must be freed with LocalFree(). | |
588 bool SetSecurityDescriptor(const wchar_t* path, PSECURITY_DESCRIPTOR* sd) { | |
589 *sd = NULL; | |
590 // We succeed without doing anything if ACLs aren't supported. | |
591 if (!IsAclSupportedForPath(path)) | |
592 return true; | |
593 | |
594 wchar_t* sid = NULL; | |
595 if (!GetCurrentOwnerSid(&sid)) | |
596 return false; | |
597 | |
598 // The largest SID is under 200 characters, so 300 should give enough slack. | |
599 StackString<300> sddl; | |
600 bool result = sddl.append(L"D:PAI" // Protected, auto-inherited DACL. | |
601 L"(A;;FA;;;BA;)" // Admin: Full control. | |
602 L"(A;OIIOCI;GA;;;BA;)" | |
603 L"(A;;FA;;;SY;)" // System: Full control. | |
604 L"(A;OIIOCI;GA;;;SY;)" | |
605 L"(A;OIIOCI;GA;;;CO;)" // Owner: Full control. | |
606 L"(A;;FA;;;") && sddl.append(sid) && sddl.append(L";)"); | |
607 if (result) { | |
608 result = ::ConvertStringSecurityDescriptorToSecurityDescriptor( | |
609 sddl.get(), SDDL_REVISION_1, sd, NULL); | |
610 } | |
611 | |
612 ::LocalFree(sid); | |
613 return result; | |
614 } | |
615 | |
542 // Creates a temporary directory under |base_path| and returns the full path | 616 // Creates a temporary directory under |base_path| and returns the full path |
543 // of created directory in |work_dir|. If successful return true, otherwise | 617 // of created directory in |work_dir|. If successful return true, otherwise |
544 // false. When successful, the returned |work_dir| will always have a trailing | 618 // false. When successful, the returned |work_dir| will always have a trailing |
545 // backslash and this function requires that |base_path| always includes a | 619 // backslash and this function requires that |base_path| always includes a |
546 // trailing backslash as well. | 620 // trailing backslash as well. |
547 // We do not use GetTempFileName here to avoid running into AV software that | 621 // We do not use GetTempFileName here to avoid running into AV software that |
548 // might hold on to the temp file as soon as we create it and then we can't | 622 // might hold on to the temp file as soon as we create it and then we can't |
549 // delete it and create a directory in its place. So, we use our own mechanism | 623 // delete it and create a directory in its place. So, we use our own mechanism |
550 // for creating a directory with a hopefully-unique name. In the case of a | 624 // for creating a directory with a hopefully-unique name. In the case of a |
551 // collision, we retry a few times with a new name before failing. | 625 // collision, we retry a few times with a new name before failing. |
552 bool CreateWorkDir(const wchar_t* base_path, PathString* work_dir) { | 626 bool CreateWorkDir(const wchar_t* base_path, PathString* work_dir, |
627 ProcessExitResult* exit_code) { | |
628 *exit_code = ProcessExitResult(PATH_STRING_OVERFLOW); | |
553 if (!work_dir->assign(base_path) || !work_dir->append(kTempPrefix)) | 629 if (!work_dir->assign(base_path) || !work_dir->append(kTempPrefix)) |
554 return false; | 630 return false; |
555 | 631 |
556 // Store the location where we'll append the id. | 632 // Store the location where we'll append the id. |
557 size_t end = work_dir->length(); | 633 size_t end = work_dir->length(); |
558 | 634 |
559 // Check if we'll have enough buffer space to continue. | 635 // Check if we'll have enough buffer space to continue. |
560 // The name of the directory will use up 11 chars and then we need to append | 636 // The name of the directory will use up 11 chars and then we need to append |
561 // the trailing backslash and a terminator. We've already added the prefix | 637 // the trailing backslash and a terminator. We've already added the prefix |
562 // to the buffer, so let's just make sure we've got enough space for the rest. | 638 // to the buffer, so let's just make sure we've got enough space for the rest. |
563 if ((work_dir->capacity() - end) < (_countof("fffff.tmp") + 1)) | 639 if ((work_dir->capacity() - end) < (_countof("fffff.tmp") + 1)) |
564 return false; | 640 return false; |
565 | 641 |
566 // Generate a unique id. We only use the lowest 20 bits, so take the top | 642 // Add an ACL if supported by the filesystem. Otherwise system-level installs |
567 // 12 bits and xor them with the lower bits. | 643 // are potentially vulnerable to file squatting attacks. |
568 DWORD id = ::GetTickCount(); | 644 SECURITY_ATTRIBUTES sa = {}; |
569 id ^= (id >> 12); | 645 sa.nLength = sizeof(SECURITY_ATTRIBUTES); |
646 if (!SetSecurityDescriptor(base_path, &sa.lpSecurityDescriptor)) { | |
647 *exit_code = ProcessExitResult(UNABLE_TO_SET_DIRECTORY_ACL, | |
648 ::GetLastError()); | |
649 return false; | |
650 } | |
570 | 651 |
571 int max_attempts = 10; | 652 unsigned int id; |
572 while (max_attempts--) { | 653 ::RtlGenRandom(&id, sizeof(id)); |
654 bool result = false; | |
655 *exit_code = ProcessExitResult(UNABLE_TO_GET_WORK_DIRECTORY); | |
656 for (int max_attempts = 10; max_attempts; --max_attempts) { | |
573 // This converts 'id' to a string in the format "78563412" on windows | 657 // This converts 'id' to a string in the format "78563412" on windows |
574 // because of little endianness, but we don't care since it's just | 658 // because of little endianness, but we don't care since it's just |
575 // a name. | 659 // a name. Since we checked capaity at the front end, we don't need to |
576 if (!HexEncode(&id, sizeof(id), work_dir->get() + end, | 660 // duplicate it here. |
577 work_dir->capacity() - end)) { | 661 HexEncode(&id, sizeof(id), work_dir->get() + end, |
578 return false; | 662 work_dir->capacity() - end); |
579 } | |
580 | 663 |
581 // We only want the first 5 digits to remain within the 8.3 file name | 664 // We only want the first 5 digits to remain within the 8.3 file name |
582 // format (compliant with previous implementation). | 665 // format (compliant with previous implementation). |
583 work_dir->truncate_at(end + 5); | 666 work_dir->truncate_at(end + 5); |
584 | 667 |
585 // for consistency with the previous implementation which relied on | 668 // for consistency with the previous implementation which relied on |
586 // GetTempFileName, we append the .tmp extension. | 669 // GetTempFileName, we append the .tmp extension. |
587 work_dir->append(L".tmp"); | 670 work_dir->append(L".tmp"); |
588 if (::CreateDirectory(work_dir->get(), NULL)) { | 671 |
672 if (::CreateDirectory(work_dir->get(), | |
673 sa.lpSecurityDescriptor ? &sa : NULL)) { | |
589 // Yay! Now let's just append the backslash and we're done. | 674 // Yay! Now let's just append the backslash and we're done. |
590 return work_dir->append(L"\\"); | 675 work_dir->append(L"\\"); |
676 *exit_code = ProcessExitResult(SUCCESS_EXIT_CODE); | |
677 break; | |
591 } | 678 } |
592 ++id; // Try a different name. | 679 ++id; // Try a different name. |
grt (UTC plus 2)
2015/12/15 20:08:10
wow, i just noticed how subtle this is. it only wo
jschuh
2015/12/15 20:58:58
I know, right? I was originally going to leave it
| |
593 } | 680 } |
594 | 681 |
595 return false; | 682 if (sa.lpSecurityDescriptor) |
683 LocalFree(sa.lpSecurityDescriptor); | |
684 return result; | |
grt (UTC plus 2)
2015/12/15 20:08:10
remove line 654 and change this to:
return exit_
jschuh
2015/12/15 20:58:58
Done (clever).
| |
596 } | 685 } |
597 | 686 |
598 // Creates and returns a temporary directory in |work_dir| that can be used to | 687 // Creates and returns a temporary directory in |work_dir| that can be used to |
599 // extract mini_installer payload. |work_dir| ends with a path separator. | 688 // extract mini_installer payload. |work_dir| ends with a path separator. |
600 bool GetWorkDir(HMODULE module, PathString* work_dir) { | 689 bool GetWorkDir(HMODULE module, PathString* work_dir, |
690 ProcessExitResult* exit_code) { | |
601 PathString base_path; | 691 PathString base_path; |
602 DWORD len = ::GetTempPath(static_cast<DWORD>(base_path.capacity()), | 692 DWORD len = ::GetTempPath(static_cast<DWORD>(base_path.capacity()), |
603 base_path.get()); | 693 base_path.get()); |
604 if (!len || len >= base_path.capacity() || | 694 if (!len || len >= base_path.capacity() || |
605 !CreateWorkDir(base_path.get(), work_dir)) { | 695 !CreateWorkDir(base_path.get(), work_dir, exit_code)) { |
606 // Problem creating the work dir under TEMP path, so try using the | 696 // Problem creating the work dir under TEMP path, so try using the |
607 // current directory as the base path. | 697 // current directory as the base path. |
608 len = ::GetModuleFileName(module, base_path.get(), | 698 len = ::GetModuleFileName(module, base_path.get(), |
609 static_cast<DWORD>(base_path.capacity())); | 699 static_cast<DWORD>(base_path.capacity())); |
610 if (len >= base_path.capacity() || !len) | 700 if (len >= base_path.capacity() || !len) |
611 return false; // Can't even get current directory? Return an error. | 701 return false; // Can't even get current directory? Return an error. |
612 | 702 |
613 wchar_t* name = GetNameFromPathExt(base_path.get(), len); | 703 wchar_t* name = GetNameFromPathExt(base_path.get(), len); |
614 if (name == base_path.get()) | 704 if (name == base_path.get()) |
615 return false; // There was no directory in the string! Bail out. | 705 return false; // There was no directory in the string! Bail out. |
616 | 706 |
617 *name = L'\0'; | 707 *name = L'\0'; |
618 | 708 |
619 return CreateWorkDir(base_path.get(), work_dir); | 709 *exit_code = ProcessExitResult(SUCCESS_EXIT_CODE); |
710 return CreateWorkDir(base_path.get(), work_dir, exit_code); | |
620 } | 711 } |
621 return true; | 712 return true; |
622 } | 713 } |
623 | 714 |
624 // Returns true for ".." and "." directories. | 715 // Returns true for ".." and "." directories. |
625 bool IsCurrentOrParentDirectory(const wchar_t* dir) { | 716 bool IsCurrentOrParentDirectory(const wchar_t* dir) { |
626 return dir && | 717 return dir && |
627 dir[0] == L'.' && | 718 dir[0] == L'.' && |
628 (dir[1] == L'\0' || (dir[1] == L'.' && dir[2] == L'\0')); | 719 (dir[1] == L'\0' || (dir[1] == L'.' && dir[2] == L'\0')); |
629 } | 720 } |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
774 if (!configuration.Initialize(module)) | 865 if (!configuration.Initialize(module)) |
775 return ProcessExitResult(GENERIC_INITIALIZATION_FAILURE); | 866 return ProcessExitResult(GENERIC_INITIALIZATION_FAILURE); |
776 | 867 |
777 // If the --cleanup switch was specified on the command line, then that means | 868 // If the --cleanup switch was specified on the command line, then that means |
778 // we should only do the cleanup and then exit. | 869 // we should only do the cleanup and then exit. |
779 if (ProcessNonInstallOperations(configuration, &exit_code)) | 870 if (ProcessNonInstallOperations(configuration, &exit_code)) |
780 return exit_code; | 871 return exit_code; |
781 | 872 |
782 // First get a path where we can extract payload | 873 // First get a path where we can extract payload |
783 PathString base_path; | 874 PathString base_path; |
784 if (!GetWorkDir(module, &base_path)) | 875 if (!GetWorkDir(module, &base_path, &exit_code)) |
785 return ProcessExitResult(UNABLE_TO_GET_WORK_DIRECTORY); | 876 return exit_code; |
786 | 877 |
787 #if defined(GOOGLE_CHROME_BUILD) | 878 #if defined(GOOGLE_CHROME_BUILD) |
788 // Set the magic suffix in registry to try full installer next time. We ignore | 879 // Set the magic suffix in registry to try full installer next time. We ignore |
789 // any errors here and we try to set the suffix for user level unless | 880 // any errors here and we try to set the suffix for user level unless |
790 // --system-level is on the command line in which case we set it for system | 881 // --system-level is on the command line in which case we set it for system |
791 // level instead. This only applies to the Google Chrome distribution. | 882 // level instead. This only applies to the Google Chrome distribution. |
792 SetInstallerFlags(configuration); | 883 SetInstallerFlags(configuration); |
793 #endif | 884 #endif |
794 | 885 |
795 PathString archive_path; | 886 PathString archive_path; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
834 #pragma function(memset) | 925 #pragma function(memset) |
835 void* memset(void* dest, int c, size_t count) { | 926 void* memset(void* dest, int c, size_t count) { |
836 void* start = dest; | 927 void* start = dest; |
837 while (count--) { | 928 while (count--) { |
838 *reinterpret_cast<char*>(dest) = static_cast<char>(c); | 929 *reinterpret_cast<char*>(dest) = static_cast<char>(c); |
839 dest = reinterpret_cast<char*>(dest) + 1; | 930 dest = reinterpret_cast<char*>(dest) + 1; |
840 } | 931 } |
841 return start; | 932 return start; |
842 } | 933 } |
843 } // extern "C" | 934 } // extern "C" |
OLD | NEW |