Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(295)

Side by Side Diff: chrome/installer/mini_installer/mini_installer.cc

Issue 1496093002: Add a restricted ACL to installer temp directory (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: nit Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/installer/mini_installer/exit_code.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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 *exit_code = ProcessExitResult(UNABLE_TO_GET_WORK_DIRECTORY);
654 for (int max_attempts = 10; max_attempts; --max_attempts) {
655 ::RtlGenRandom(&id, sizeof(id)); // Try a different name.
656
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.
593 } 679 }
594 680
595 return false; 681 if (sa.lpSecurityDescriptor)
682 LocalFree(sa.lpSecurityDescriptor);
683 return exit_code->IsSuccess();
596 } 684 }
597 685
598 // Creates and returns a temporary directory in |work_dir| that can be used to 686 // 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. 687 // extract mini_installer payload. |work_dir| ends with a path separator.
600 bool GetWorkDir(HMODULE module, PathString* work_dir) { 688 bool GetWorkDir(HMODULE module, PathString* work_dir,
689 ProcessExitResult* exit_code) {
601 PathString base_path; 690 PathString base_path;
602 DWORD len = ::GetTempPath(static_cast<DWORD>(base_path.capacity()), 691 DWORD len = ::GetTempPath(static_cast<DWORD>(base_path.capacity()),
603 base_path.get()); 692 base_path.get());
604 if (!len || len >= base_path.capacity() || 693 if (!len || len >= base_path.capacity() ||
605 !CreateWorkDir(base_path.get(), work_dir)) { 694 !CreateWorkDir(base_path.get(), work_dir, exit_code)) {
606 // Problem creating the work dir under TEMP path, so try using the 695 // Problem creating the work dir under TEMP path, so try using the
607 // current directory as the base path. 696 // current directory as the base path.
608 len = ::GetModuleFileName(module, base_path.get(), 697 len = ::GetModuleFileName(module, base_path.get(),
609 static_cast<DWORD>(base_path.capacity())); 698 static_cast<DWORD>(base_path.capacity()));
610 if (len >= base_path.capacity() || !len) 699 if (len >= base_path.capacity() || !len)
611 return false; // Can't even get current directory? Return an error. 700 return false; // Can't even get current directory? Return an error.
612 701
613 wchar_t* name = GetNameFromPathExt(base_path.get(), len); 702 wchar_t* name = GetNameFromPathExt(base_path.get(), len);
614 if (name == base_path.get()) 703 if (name == base_path.get())
615 return false; // There was no directory in the string! Bail out. 704 return false; // There was no directory in the string! Bail out.
616 705
617 *name = L'\0'; 706 *name = L'\0';
618 707
619 return CreateWorkDir(base_path.get(), work_dir); 708 *exit_code = ProcessExitResult(SUCCESS_EXIT_CODE);
709 return CreateWorkDir(base_path.get(), work_dir, exit_code);
620 } 710 }
621 return true; 711 return true;
622 } 712 }
623 713
624 // Returns true for ".." and "." directories. 714 // Returns true for ".." and "." directories.
625 bool IsCurrentOrParentDirectory(const wchar_t* dir) { 715 bool IsCurrentOrParentDirectory(const wchar_t* dir) {
626 return dir && 716 return dir &&
627 dir[0] == L'.' && 717 dir[0] == L'.' &&
628 (dir[1] == L'\0' || (dir[1] == L'.' && dir[2] == L'\0')); 718 (dir[1] == L'\0' || (dir[1] == L'.' && dir[2] == L'\0'));
629 } 719 }
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after
774 if (!configuration.Initialize(module)) 864 if (!configuration.Initialize(module))
775 return ProcessExitResult(GENERIC_INITIALIZATION_FAILURE); 865 return ProcessExitResult(GENERIC_INITIALIZATION_FAILURE);
776 866
777 // If the --cleanup switch was specified on the command line, then that means 867 // If the --cleanup switch was specified on the command line, then that means
778 // we should only do the cleanup and then exit. 868 // we should only do the cleanup and then exit.
779 if (ProcessNonInstallOperations(configuration, &exit_code)) 869 if (ProcessNonInstallOperations(configuration, &exit_code))
780 return exit_code; 870 return exit_code;
781 871
782 // First get a path where we can extract payload 872 // First get a path where we can extract payload
783 PathString base_path; 873 PathString base_path;
784 if (!GetWorkDir(module, &base_path)) 874 if (!GetWorkDir(module, &base_path, &exit_code))
785 return ProcessExitResult(UNABLE_TO_GET_WORK_DIRECTORY); 875 return exit_code;
786 876
787 #if defined(GOOGLE_CHROME_BUILD) 877 #if defined(GOOGLE_CHROME_BUILD)
788 // Set the magic suffix in registry to try full installer next time. We ignore 878 // 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 879 // 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 880 // --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. 881 // level instead. This only applies to the Google Chrome distribution.
792 SetInstallerFlags(configuration); 882 SetInstallerFlags(configuration);
793 #endif 883 #endif
794 884
795 PathString archive_path; 885 PathString archive_path;
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
834 #pragma function(memset) 924 #pragma function(memset)
835 void* memset(void* dest, int c, size_t count) { 925 void* memset(void* dest, int c, size_t count) {
836 void* start = dest; 926 void* start = dest;
837 while (count--) { 927 while (count--) {
838 *reinterpret_cast<char*>(dest) = static_cast<char>(c); 928 *reinterpret_cast<char*>(dest) = static_cast<char>(c);
839 dest = reinterpret_cast<char*>(dest) + 1; 929 dest = reinterpret_cast<char*>(dest) + 1;
840 } 930 }
841 return start; 931 return start;
842 } 932 }
843 } // extern "C" 933 } // extern "C"
OLDNEW
« no previous file with comments | « chrome/installer/mini_installer/exit_code.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698